@leofcoin/peernet 0.9.6 → 0.9.7

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.
@@ -264,7 +264,7 @@ class LeofcoinStorage$1 {
264
264
 
265
265
  }
266
266
 
267
- var version = "0.9.4";
267
+ var version = "0.9.6";
268
268
 
269
269
  var api$1 = {
270
270
  version: ({send}) => send({client: '@peernet/api/http', version}),
@@ -1253,17 +1253,13 @@ class DhtEarth {
1253
1253
  const peerLoc = await this.getCoordinates(address);
1254
1254
 
1255
1255
  for (const provider of providers) {
1256
- all.push(this.getDistance(peerLoc, provider));
1256
+ if (provider.address === '127.0.0.1') all.push({provider, distance: 0});
1257
+ else all.push(this.getDistance(peerLoc, provider));
1257
1258
  }
1258
1259
 
1259
1260
  all = await Promise.all(all);
1260
-
1261
- const closestPeer = all.reduce((p, c) => {
1262
- if (!c.distance || c.distance === NaN) c.distance = 0;
1263
- if (c.distance < p || p === 0) return c.provider;
1264
- }, 0);
1265
-
1266
- return closestPeer;
1261
+ all = all.sort((previous, current) => previous.distance - current.distance);
1262
+ return all[0].provider;
1267
1263
  }
1268
1264
 
1269
1265
  /**
@@ -1391,656 +1387,656 @@ const nothingFoundError = (hash) => {
1391
1387
  return new Error(`nothing found for ${hash}`)
1392
1388
  };
1393
1389
 
1394
- globalThis.leofcoin = globalThis.leofcoin || {};
1395
- globalThis.globalSub = globalThis.globalSub || new PubSub__default['default']({verbose: true});
1396
-
1397
- /**
1398
- * @access public
1399
- * @example
1400
- * const peernet = new Peernet();
1401
- */
1402
- class Peernet {
1403
- /**
1404
- * @access public
1405
- * @param {Object} options
1406
- * @param {String} options.network - desired network
1407
- * @param {String} options.root - path to root directory
1408
- * @param {String} options.storePrefix - prefix for datatores (lfc)
1409
- *
1410
- * @return {Promise} instance of Peernet
1411
- *
1412
- * @example
1413
- * const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
1414
- */
1415
- constructor(options = {}) {
1416
- this._discovered = [];
1417
- /**
1418
- * @property {String} network - current network
1419
- */
1420
- this.network = options.network || 'leofcoin';
1421
- const parts = this.network.split(':');
1422
-
1423
- if (!options.storePrefix) options.storePrefix = 'lfc';
1424
- if (!options.port) options.port = 2000;
1425
- if (!options.root) {
1426
- if (parts[1]) options.root = `.${parts[0]}/peernet/${parts[1]}`;
1427
- else options.root = `.${this.network}/peernet`;
1428
- }
1429
- globalThis.peernet = this;
1430
- return this._init(options)
1431
- }
1432
-
1433
- get defaultStores() {
1434
- return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
1435
- }
1436
-
1437
- addProto(name, proto) {
1438
- if (!this.protos[name]) this.protos[name] = proto;
1439
- }
1440
-
1441
- addCodec(name, codec) {
1442
- if (!this.codecs[name]) this.codecs[name] = codec;
1443
- }
1444
-
1445
- async addStore(name, prefix, root, isPrivate = true) {
1446
- if (name === 'block' || name === 'transaction' || name === 'chain' ||
1447
- name === 'data' || name === 'message') isPrivate = false;
1448
-
1449
- let Storage;
1450
- if (this.hasDaemon) {
1451
- Storage = LeofcoinStorageClient;
1452
- } else {
1453
- Storage = LeofcoinStorage$1;
1454
- }
1455
- globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
1456
- await new Storage(`${prefix}-${name}`, root);
1457
-
1458
- globalThis[`${name}Store`].private = isPrivate;
1459
- if (!isPrivate) this.stores.push(name);
1460
- }
1461
-
1462
-
1463
- /**
1464
- * @see MessageHandler
1465
- */
1466
- prepareMessage(to, data) {
1467
- return this._messageHandler.prepareMessage(this.id, to, data)
1468
- }
1469
-
1470
- /**
1471
- * @access public
1472
- *
1473
- * @return {Array} peerId
1474
- */
1475
- get peers() {
1476
- return [...connections.values()]
1477
- }
1478
-
1479
- /**
1480
- * @private
1481
- *
1482
- * @param {Object} options
1483
- * @param {String} options.root - path to root directory
1484
- *
1485
- * @return {Promise} instance of Peernet
1486
- */
1487
- async _init(options) {
1488
- // peernetDHT aka closesPeer by coordinates
1489
- /**
1490
- * @type {Object}
1491
- * @property {Object} peer Instance of Peer
1492
- */
1493
- this.dht = new DhtEarth();
1494
- /**
1495
- * @type {Map}
1496
- * @property {Object} peer Instance of Peer
1497
- */
1498
- this.peerMap = new Map();
1499
- this.stores = [];
1500
- this.requestProtos = {};
1501
- this.storePrefix = options.storePrefix;
1502
- this.root = options.root;
1503
-
1504
- /**
1505
- * proto Object containing protos
1506
- * @type {Object}
1507
- * @property {PeernetMessage} protos[peernet-message] messageNode
1508
- * @property {DHTMessage} protos[peernet-dht] messageNode
1509
- * @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
1510
- * @property {DataMessage} protos[peernet-data] messageNode
1511
- * @property {DataMessageResponse} protos[peernet-data-response] messageNode
1512
- */
1513
- globalThis.peernet.protos = {
1514
- 'peernet-request': request,
1515
- 'peernet-response': response,
1516
- 'peernet-peer': PeerMessage,
1517
- 'peernet-peer-response': PeerMessageResponse,
1518
- 'peernet-message': peernetMessage,
1519
- 'peernet-dht': dht,
1520
- 'peernet-dht-response': dhtResponse,
1521
- 'peernet-data': DataMessage,
1522
- 'peernet-data-response': DataMessageResponse,
1523
- 'peernet-ps': PsMessage,
1524
- 'chat-message': ChatMessage,
1525
- };
1526
-
1527
- this.protos = globalThis.peernet.protos;
1528
- this.codecs = codec.codecs;
1529
-
1530
- this._messageHandler = new MessageHandler(this.network);
1531
-
1532
- const {daemon, environment} = await target();
1533
- this.hasDaemon = daemon;
1534
-
1535
- if (this.hasDaemon) {
1536
- globalThis.peernet.client = await httpClient({
1537
- protocol: 'peernet-v0.1.0', host: '127.0.0.1', port: options.port,
1538
- });
1539
- } else {
1540
- if (environment !== 'browser') http(options);
1541
- }
1542
-
1543
- for (const store of this.defaultStores) {
1544
- await this.addStore(store, options.storePrefix, options.root);
1545
- }
1546
-
1547
- try {
1548
- const pub = await accountStore.get('public');
1549
- this.id = pub.walletId;
1550
- } catch (e) {
1551
- if (e.code === 'ERR_NOT_FOUND') {
1552
- const wallet = {};
1553
- const {identity, accounts, config} = await generateAccount(this.network);
1554
- wallet.identity = identity;
1555
- wallet.accounts = accounts;
1556
- wallet.version = 1;
1557
- walletStore.put(wallet);
1558
- await accountStore.put('config', config);
1559
- await accountStore.put('public', {walletId: wallet.identity.walletId});
1560
-
1561
- this.id = wallet.identity.walletId;
1562
- } else {
1563
- throw e
1564
- }
1565
- }
1566
- this._peerHandler = new PeerDiscovery(this.id);
1567
- // peernet id
1568
- const id = Buffer.from(this.id.slice(0, 32));
1569
- this.peerId = id;
1570
-
1571
- pubsub.subscribe('peer:discovered', async (peer) => {
1572
- this._peerHandler.discover(peer);
1573
- peer.on('peernet.data', async (message) => {
1574
- const id = message.id;
1575
- message = new peernetMessage(Buffer.from(message.data.data));
1576
- const proto = protoFor(message.decoded.data);
1577
- await this._protoHandler({id, proto}, peer);
1578
- const fulldId = this._getPeerId(peer.id);
1579
- if (fulldId && this._discovered.indexOf(peer.id) === -1) {
1580
- this._discovered.push(peer.id);
1581
- pubsub.publish('peer:connected', peer);
1582
- }
1583
- });
1584
- });
1585
- pubsub.subscribe('peer:disconnected', async (peer) => {
1586
- let index = this._discovered.indexOf(peer.id);
1587
- if (index !== -1) this._discovered.splice(index, 1);
1588
- const id = this._getPeerId(peer.id);
1589
- let peerIds = this.peerMap.get(id);
1590
-
1591
- if (peerIds) {
1592
- index = peerIds.indexOf(peer.id);
1593
- if (index !== -1) peerIds.splice(index, 1);
1594
- } else {
1595
- peerIds = [];
1596
- }
1597
-
1598
- if (peerIds.length === 0) this.peerMap.delete(id);
1599
- else this.peerMap.set(id, peerIds);
1600
- });
1601
- pubsub.subscribe('peer:connected', async (peer) => {
1602
- console.log({connected: peer.id, as: this._getPeerId(peer.id) });
1603
- // peer.on('peernet.data', async (message) => {
1604
- // const id = message.id
1605
- // message = new PeernetMessage(Buffer.from(message.data.data))
1606
- // const proto = protoFor(message.decoded.data)
1607
- // this._protoHandler({id, proto}, peer)
1608
- // })
1609
- });
1610
-
1611
- /**
1612
- * @access public
1613
- * @type {PeernetClient}
1614
- */
1615
- this.client = new PeernetClient({...options, id});
1616
- return this
1617
- }
1618
-
1619
- _getPeerId(id) {
1620
- for (const entry of [...this.peerMap.entries()]) {
1621
- for (const _id of entry[1]) {
1622
- if (_id === id) return entry[0]
1623
- }
1624
- }
1625
- }
1626
-
1627
- addRequestHandler(name, method) {
1628
- this.requestProtos[name] = method;
1629
- }
1630
-
1631
- /**
1632
- * @private
1633
- *
1634
- * @param {Buffer} message - peernet message
1635
- * @param {PeernetPeer} peer - peernet peer
1636
- */
1637
- async _protoHandler(message, peer) {
1638
- const {id, proto} = message;
1639
- if (proto.name === 'peernet-peer') {
1640
- const from = proto.decoded.id;
1641
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1642
- else {
1643
- const connections = this.peerMap.get(from);
1644
- if (connections.indexOf(peer.id) === -1) {
1645
- connections.push(peer.id);
1646
- this.peerMap.set(from, connections);
1647
- }
1648
- }
1649
- const data = new PeerMessageResponse({id: this.id});
1650
- const node = await this.prepareMessage(from, data.encoded);
1651
-
1652
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1653
- } else if (proto.name === 'peernet-peer-response') {
1654
- const from = proto.decoded.id;
1655
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1656
- else {
1657
- const connections = this.peerMap.get(from);
1658
- if (connections.indexOf(peer.id) === -1) {
1659
- connections.push(peer.id);
1660
- this.peerMap.set(from, connections);
1661
- }
1662
- }
1663
- } else {
1664
- let from = this._getPeerId(peer.id);
1665
- if (!from) {
1666
- const data = new PeerMessage({id: this.id});
1667
- const node = await this.prepareMessage(peer.id, data.encoded);
1668
-
1669
- let response = await peer.request(node.encoded);
1670
- response = protoFor(response);
1671
- response = new PeerMessageResponse(response.decoded.data);
1672
-
1673
- from = response.decoded.id;
1674
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1675
- else {
1676
- const connections = this.peerMap.get(from);
1677
- if (connections.indexOf(peer.id) === -1) {
1678
- connections.push(peer.id);
1679
- this.peerMap.set(from, connections);
1680
- }
1681
- }
1682
- }
1683
- if (proto.name === 'peernet-dht') {
1684
- let { hash, store } = proto.decoded;
1685
- let has;
1686
-
1687
- if (!store) {
1688
- has = await this.has(hash);
1689
- } else {
1690
- store = globalThis[`${store}Store`];
1691
- if (store.private) has = false;
1692
- else has = await store.has(hash);
1693
- }
1694
- const data = new dhtResponse({hash, has});
1695
- const node = await this.prepareMessage(from, data.encoded);
1696
-
1697
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1698
- } else if (proto.name === 'peernet-data') {
1699
- let { hash, store } = proto.decoded;
1700
- let data;
1701
-
1702
- if (!store) {
1703
- data = await this.get(hash);
1704
- } else {
1705
- store = globalThis[`${store}Store`];
1706
- if (store.private) {
1707
- // TODO: ban
1708
- return
1709
- } else data = await store.get(hash);
1710
- }
1711
-
1712
- if (data) {
1713
- data = new DataMessageResponse({hash, data: Buffer.from(data)});
1714
-
1715
- const node = await this.prepareMessage(from, data.encoded);
1716
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1717
- }
1718
- } else if (proto.name === 'peernet-peer') {
1719
- const from = proto.decoded.id;
1720
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1721
- else {
1722
- const connections = this.peerMap.get(from);
1723
- connections.push(peer.id);
1724
- this.peerMap.set(from, connections);
1725
- }
1726
- const data = new PeerMessage({id: this.id});
1727
- const node = await this.prepareMessage(from, data.encoded);
1728
-
1729
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1730
- } else if (proto.name === 'peernet-request') {
1731
- // TODO: make dynamic
1732
- // exposeddevapi[proto.decoded.request](proto.decoded.params)
1733
- const method = this.requestProtos[proto.decoded.request];
1734
- if (method) {
1735
- const data = await method();
1736
- const node = await this.prepareMessage(from, data.encoded);
1737
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1738
- }
1739
- } else if (proto.name === 'peernet-ps' &&
1740
- this._getPeerId(peer.id) !== this.id.toString()) {
1741
- globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString());
1742
- }
1743
- }
1744
- }
1745
-
1746
- /**
1747
- * performs a walk and resolves first encounter
1748
- *
1749
- * @param {String} hash
1750
- */
1751
- async walk(hash) {
1752
- if (!hash) throw new Error('hash expected, received undefined')
1753
- const data = new dht({hash});
1754
- this.client.id;
1755
- for (const peer of this.peers) {
1756
- const node = await this.prepareMessage(peer.id, data.encoded);
1757
-
1758
- const result = await peer.request(node.encoded);
1759
-
1760
- let proto = protoFor(result.data);
1761
-
1762
- if (proto.name !== 'peernet-message') throw encapsulatedError()
1763
- const from = proto.decoded.from;
1764
- proto = protoFor(proto.decoded.data);
1765
-
1766
- if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
1767
-
1768
- // TODO: give ip and port (just used for location)
1769
- if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
1770
- peer.connection.remoteFamily = 'ipv4';
1771
- peer.connection.remoteAddress = '127.0.0.1';
1772
- peer.connection.remotePort = '0000';
1773
- }
1774
-
1775
- const peerInfo = {
1776
- family: peer.connection.remoteFamily || peer.connection.localFamily,
1777
- address: peer.connection.remoteAddress || peer.connection.localAddress,
1778
- port: peer.connection.remotePort || peer.connection.localPort,
1779
- id: from,
1780
- };
1781
-
1782
- if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash);
1783
- }
1784
- return
1785
- }
1786
-
1787
- /**
1788
- * Override DHT behavior, try's finding the content three times
1789
- *
1790
- * @param {String} hash
1791
- */
1792
- async providersFor(hash) {
1793
- let providers = await this.dht.providersFor(hash);
1794
- // walk the network to find a provider
1795
- if (!providers || providers.length === 0) {
1796
- await this.walk(hash);
1797
- providers = await this.dht.providersFor(hash);
1798
- // second walk the network to find a provider
1799
- if (!providers || providers.length === 0) {
1800
- await this.walk(hash);
1801
- providers = await this.dht.providersFor(hash);
1802
- }
1803
- // last walk
1804
- if (!providers || providers.length === 0) {
1805
- await this.walk(hash);
1806
- providers = await this.dht.providersFor(hash);
1807
- }
1808
- }
1809
- // undefined if no providers given
1810
- return providers
1811
- }
1812
-
1813
- get block() {
1814
- return {
1815
- get: async (hash) => {
1816
- const data = await blockStore.has(hash);
1817
- if (data) return await blockStore.get(hash)
1818
- return this.requestData(hash)
1819
- },
1820
- put: async (hash, data) => {
1821
- if (await blockStore.has(hash)) return
1822
- return await blockStore.put(hash, data)
1823
- },
1824
- has: async (hash) => await blockStore.has(hash, 'block'),
1825
- }
1826
- }
1827
-
1828
- get transaction() {
1829
- return {
1830
- get: async (hash) => {
1831
- const data = await transactionStore.has(hash);
1832
- if (data) return await transactionStore.get(hash)
1833
- return this.requestData(hash, 'transaction')
1834
- },
1835
- put: async (hash, data) => {
1836
- if (await transactionStore.has(hash)) return
1837
- return await transactionStore.put(hash, data)
1838
- },
1839
- has: async (hash) => await transactionStore.has(hash),
1840
- }
1841
- }
1842
-
1843
- async requestData(hash, store) {
1844
- const providers = await this.providersFor(hash);
1845
- if (!providers || providers.size === 0) throw nothingFoundError(hash)
1846
- debug(`found ${providers.size} provider(s) for ${hash}`);
1847
- // get closest peer on earth
1848
- const closestPeer = await this.dht.closestPeer(providers);
1849
- // get peer instance by id
1850
- if (!closestPeer || !closestPeer.id) return this.requestData(hash, store)
1851
-
1852
- const id = closestPeer.id.toString();
1853
- if (this.peers) {
1854
- let closest = this.peers.filter((peer) => {
1855
- if (this._getPeerId(peer.id) === id) return peer
1856
- });
1857
-
1858
- let data = new DataMessage({hash, store});
1859
-
1860
- const node = await this.prepareMessage(id, data.encoded);
1861
- if (closest[0]) data = await closest[0].request(node.encoded);
1862
- else {
1863
- closest = this.peers.filter((peer) => {
1864
- if (peer.id.toString() === id) return peer
1865
- });
1866
- if (closest[0]) data = await closest[0].request(node.encoded);
1867
- }
1868
- if (data.data) {
1869
- let proto = protoFor(Buffer.from(data.data));
1870
- proto = protoFor(proto.decoded.data);
1871
- return proto.decoded.data
1872
- }
1873
-
1874
- // this.put(hash, proto.decoded.data)
1875
- }
1876
- return null
1877
- }
1878
-
1879
-
1880
- get message() {
1881
- return {
1882
- /**
1883
- * Get content for given message hash
1884
- *
1885
- * @param {String} hash
1886
- */
1887
- get: async (hash) => {
1888
- debug(`get message ${hash}`);
1889
- const message = await messageStore.has(hash);
1890
- if (message) return await messageStore.get(hash)
1891
- return this.requestData(hash, 'message')
1892
- },
1893
- /**
1894
- * put message content
1895
- *
1896
- * @param {String} hash
1897
- * @param {Buffer} message
1898
- */
1899
- put: async (hash, message) => await messageStore.put(hash, message),
1900
- /**
1901
- * @param {String} hash
1902
- * @return {Boolean}
1903
- */
1904
- has: async (hash) => await messageStore.has(hash),
1905
- }
1906
- }
1907
-
1908
- get data() {
1909
- return {
1910
- /**
1911
- * Get content for given data hash
1912
- *
1913
- * @param {String} hash
1914
- */
1915
- get: async (hash) => {
1916
- debug(`get data ${hash}`);
1917
- const data = await dataStore.has(hash);
1918
- if (data) return await dataStore.get(hash)
1919
- return this.requestData(hash, 'data')
1920
- },
1921
- /**
1922
- * put data content
1923
- *
1924
- * @param {String} hash
1925
- * @param {Buffer} data
1926
- */
1927
- put: async (hash, data) => await dataStore.put(hash, data),
1928
- /**
1929
- * @param {String} hash
1930
- * @return {Boolean}
1931
- */
1932
- has: async (hash) => await dataStore.has(hash),
1933
- }
1934
- }
1935
-
1936
- /**
1937
- * goes trough given stores and tries to find data for given hash
1938
- * @param {Array} stores
1939
- * @param {string} hash
1940
- */
1941
- async whichStore(stores, hash) {
1942
- let store = stores.pop();
1943
- store = globalThis[`${store}Store`];
1944
- if (store) {
1945
- const has = await store.has(hash);
1946
- if (has) return store
1947
- if (stores.length > 0) return this.whichStore(stores, hash)
1948
- } else return null
1949
- }
1950
-
1951
- /**
1952
- * Get content for given hash
1953
- *
1954
- * @param {String} hash
1955
- */
1956
- async get(hash, store) {
1957
- debug(`get ${hash}`);
1958
- let data;
1959
- if (store) store = globalThis[`${store}Store`];
1960
- if (!store) store = await this.whichStore([...this.stores], hash);
1961
- if (store && await store.has(hash)) data = await store.get(hash);
1962
- if (data) return data
1963
-
1964
- return this.requestData(hash, 'data')
1965
- }
1966
-
1967
- /**
1968
- * put content
1969
- *
1970
- * @param {String} hash
1971
- * @param {Buffer} data
1972
- */
1973
- async put(hash, data, store = 'data') {
1974
- store = globalThis[`${store}Store`];
1975
- return await store.put(hash, data)
1976
- }
1977
-
1978
- /**
1979
- * @param {String} hash
1980
- * @return {Boolean}
1981
- */
1982
- async has(hash) {
1983
- const store = await this.whichStore([...this.stores], hash);
1984
- if (store) {
1985
- if (store.private) return false
1986
- else return true
1987
- }
1988
- return false
1989
- }
1990
-
1991
- /**
1992
- *
1993
- * @param {String} topic
1994
- * @param {String|Object|Array|Boolean|Buffer} data
1995
- */
1996
- async publish(topic, data) {
1997
- // globalSub.publish(topic, data)
1998
- if (!Buffer.isBuffer(topic)) topic = Buffer.from(topic);
1999
- if (!Buffer.isBuffer(data)) data = Buffer.from(data);
2000
- const id = Math.random().toString(36).slice(-12);
2001
- data = new PsMessage({data, topic});
2002
- for (const peer of this.peers) {
2003
- if (peer.connection._connected) {
2004
- if (peer.id.toString() !== this.peerId.toString()) {
2005
- const node = await this.prepareMessage(peer.id, data.encoded);
2006
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
2007
- }
2008
- } else {
2009
- this.removePeer(peer);
2010
- }
2011
- // TODO: if peer subscribed
2012
- }
2013
- }
2014
-
2015
- createHash(data, name) {
2016
- return new hash(data, {name})
2017
- }
2018
-
2019
- /**
2020
- *
2021
- * @param {String} topic
2022
- * @param {Method} cb
2023
- */
2024
- async subscribe(topic, cb) {
2025
- // TODO: if peer subscribed
2026
- globalSub.subscribe(topic, cb);
2027
- }
2028
-
2029
- async removePeer(peer) {
2030
- connections.delete(peer.id);
2031
- }
2032
-
2033
- get Buffer() {
2034
- return Buffer
2035
- }
2036
- // async block(index) {
2037
- // const _values = []
2038
- // for (const peer of this.peers) {
2039
- // const value = await peer.request({type: 'block', index})
2040
- // console.log(value);
2041
- // }
2042
- //
2043
- // }
1390
+ globalThis.leofcoin = globalThis.leofcoin || {};
1391
+ globalThis.globalSub = globalThis.globalSub || new PubSub__default['default']({verbose: true});
1392
+
1393
+ /**
1394
+ * @access public
1395
+ * @example
1396
+ * const peernet = new Peernet();
1397
+ */
1398
+ class Peernet {
1399
+ /**
1400
+ * @access public
1401
+ * @param {Object} options
1402
+ * @param {String} options.network - desired network
1403
+ * @param {String} options.root - path to root directory
1404
+ * @param {String} options.storePrefix - prefix for datatores (lfc)
1405
+ *
1406
+ * @return {Promise} instance of Peernet
1407
+ *
1408
+ * @example
1409
+ * const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
1410
+ */
1411
+ constructor(options = {}) {
1412
+ this._discovered = [];
1413
+ /**
1414
+ * @property {String} network - current network
1415
+ */
1416
+ this.network = options.network || 'leofcoin';
1417
+ const parts = this.network.split(':');
1418
+
1419
+ if (!options.storePrefix) options.storePrefix = 'lfc';
1420
+ if (!options.port) options.port = 2000;
1421
+ if (!options.root) {
1422
+ if (parts[1]) options.root = `.${parts[0]}/peernet/${parts[1]}`;
1423
+ else options.root = `.${this.network}/peernet`;
1424
+ }
1425
+ globalThis.peernet = this;
1426
+ return this._init(options)
1427
+ }
1428
+
1429
+ get defaultStores() {
1430
+ return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
1431
+ }
1432
+
1433
+ addProto(name, proto) {
1434
+ if (!this.protos[name]) this.protos[name] = proto;
1435
+ }
1436
+
1437
+ addCodec(name, codec) {
1438
+ if (!this.codecs[name]) this.codecs[name] = codec;
1439
+ }
1440
+
1441
+ async addStore(name, prefix, root, isPrivate = true) {
1442
+ if (name === 'block' || name === 'transaction' || name === 'chain' ||
1443
+ name === 'data' || name === 'message') isPrivate = false;
1444
+
1445
+ let Storage;
1446
+ if (this.hasDaemon) {
1447
+ Storage = LeofcoinStorageClient;
1448
+ } else {
1449
+ Storage = LeofcoinStorage$1;
1450
+ }
1451
+ globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
1452
+ await new Storage(`${prefix}-${name}`, root);
1453
+
1454
+ globalThis[`${name}Store`].private = isPrivate;
1455
+ if (!isPrivate) this.stores.push(name);
1456
+ }
1457
+
1458
+
1459
+ /**
1460
+ * @see MessageHandler
1461
+ */
1462
+ prepareMessage(to, data) {
1463
+ return this._messageHandler.prepareMessage(this.id, to, data)
1464
+ }
1465
+
1466
+ /**
1467
+ * @access public
1468
+ *
1469
+ * @return {Array} peerId
1470
+ */
1471
+ get peers() {
1472
+ return [...connections.values()]
1473
+ }
1474
+
1475
+ /**
1476
+ * @private
1477
+ *
1478
+ * @param {Object} options
1479
+ * @param {String} options.root - path to root directory
1480
+ *
1481
+ * @return {Promise} instance of Peernet
1482
+ */
1483
+ async _init(options) {
1484
+ // peernetDHT aka closesPeer by coordinates
1485
+ /**
1486
+ * @type {Object}
1487
+ * @property {Object} peer Instance of Peer
1488
+ */
1489
+ this.dht = new DhtEarth();
1490
+ /**
1491
+ * @type {Map}
1492
+ * @property {Object} peer Instance of Peer
1493
+ */
1494
+ this.peerMap = new Map();
1495
+ this.stores = [];
1496
+ this.requestProtos = {};
1497
+ this.storePrefix = options.storePrefix;
1498
+ this.root = options.root;
1499
+
1500
+ /**
1501
+ * proto Object containing protos
1502
+ * @type {Object}
1503
+ * @property {PeernetMessage} protos[peernet-message] messageNode
1504
+ * @property {DHTMessage} protos[peernet-dht] messageNode
1505
+ * @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
1506
+ * @property {DataMessage} protos[peernet-data] messageNode
1507
+ * @property {DataMessageResponse} protos[peernet-data-response] messageNode
1508
+ */
1509
+ globalThis.peernet.protos = {
1510
+ 'peernet-request': request,
1511
+ 'peernet-response': response,
1512
+ 'peernet-peer': PeerMessage,
1513
+ 'peernet-peer-response': PeerMessageResponse,
1514
+ 'peernet-message': peernetMessage,
1515
+ 'peernet-dht': dht,
1516
+ 'peernet-dht-response': dhtResponse,
1517
+ 'peernet-data': DataMessage,
1518
+ 'peernet-data-response': DataMessageResponse,
1519
+ 'peernet-ps': PsMessage,
1520
+ 'chat-message': ChatMessage,
1521
+ };
1522
+
1523
+ this.protos = globalThis.peernet.protos;
1524
+ this.codecs = codec.codecs;
1525
+
1526
+ this._messageHandler = new MessageHandler(this.network);
1527
+
1528
+ const {daemon, environment} = await target();
1529
+ this.hasDaemon = daemon;
1530
+
1531
+ if (this.hasDaemon) {
1532
+ globalThis.peernet.client = await httpClient({
1533
+ protocol: 'peernet-v0.1.0', host: '127.0.0.1', port: options.port,
1534
+ });
1535
+ } else {
1536
+ if (environment !== 'browser') http(options);
1537
+ }
1538
+
1539
+ for (const store of this.defaultStores) {
1540
+ await this.addStore(store, options.storePrefix, options.root);
1541
+ }
1542
+
1543
+ try {
1544
+ const pub = await accountStore.get('public');
1545
+ this.id = pub.walletId;
1546
+ } catch (e) {
1547
+ if (e.code === 'ERR_NOT_FOUND') {
1548
+ const wallet = {};
1549
+ const {identity, accounts, config} = await generateAccount(this.network);
1550
+ wallet.identity = identity;
1551
+ wallet.accounts = accounts;
1552
+ wallet.version = 1;
1553
+ walletStore.put(wallet);
1554
+ await accountStore.put('config', config);
1555
+ await accountStore.put('public', {walletId: wallet.identity.walletId});
1556
+
1557
+ this.id = wallet.identity.walletId;
1558
+ } else {
1559
+ throw e
1560
+ }
1561
+ }
1562
+ this._peerHandler = new PeerDiscovery(this.id);
1563
+ // peernet id
1564
+ const id = Buffer.from(this.id.slice(0, 32));
1565
+ this.peerId = id;
1566
+
1567
+ pubsub.subscribe('peer:discovered', async (peer) => {
1568
+ this._peerHandler.discover(peer);
1569
+ peer.on('peernet.data', async (message) => {
1570
+ const id = message.id;
1571
+ message = new peernetMessage(Buffer.from(message.data.data));
1572
+ const proto = protoFor(message.decoded.data);
1573
+ await this._protoHandler({id, proto}, peer);
1574
+ const fulldId = this._getPeerId(peer.id);
1575
+ if (fulldId && this._discovered.indexOf(peer.id) === -1) {
1576
+ this._discovered.push(peer.id);
1577
+ pubsub.publish('peer:connected', peer);
1578
+ }
1579
+ });
1580
+ });
1581
+ pubsub.subscribe('peer:disconnected', async (peer) => {
1582
+ let index = this._discovered.indexOf(peer.id);
1583
+ if (index !== -1) this._discovered.splice(index, 1);
1584
+ const id = this._getPeerId(peer.id);
1585
+ let peerIds = this.peerMap.get(id);
1586
+
1587
+ if (peerIds) {
1588
+ index = peerIds.indexOf(peer.id);
1589
+ if (index !== -1) peerIds.splice(index, 1);
1590
+ } else {
1591
+ peerIds = [];
1592
+ }
1593
+
1594
+ if (peerIds.length === 0) this.peerMap.delete(id);
1595
+ else this.peerMap.set(id, peerIds);
1596
+ });
1597
+ pubsub.subscribe('peer:connected', async (peer) => {
1598
+ console.log({connected: peer.id, as: this._getPeerId(peer.id) });
1599
+ // peer.on('peernet.data', async (message) => {
1600
+ // const id = message.id
1601
+ // message = new PeernetMessage(Buffer.from(message.data.data))
1602
+ // const proto = protoFor(message.decoded.data)
1603
+ // this._protoHandler({id, proto}, peer)
1604
+ // })
1605
+ });
1606
+
1607
+ /**
1608
+ * @access public
1609
+ * @type {PeernetClient}
1610
+ */
1611
+ this.client = new PeernetClient({...options, id});
1612
+ return this
1613
+ }
1614
+
1615
+ _getPeerId(id) {
1616
+ for (const entry of [...this.peerMap.entries()]) {
1617
+ for (const _id of entry[1]) {
1618
+ if (_id === id) return entry[0]
1619
+ }
1620
+ }
1621
+ }
1622
+
1623
+ addRequestHandler(name, method) {
1624
+ this.requestProtos[name] = method;
1625
+ }
1626
+
1627
+ /**
1628
+ * @private
1629
+ *
1630
+ * @param {Buffer} message - peernet message
1631
+ * @param {PeernetPeer} peer - peernet peer
1632
+ */
1633
+ async _protoHandler(message, peer) {
1634
+ const {id, proto} = message;
1635
+ if (proto.name === 'peernet-peer') {
1636
+ const from = proto.decoded.id;
1637
+ if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1638
+ else {
1639
+ const connections = this.peerMap.get(from);
1640
+ if (connections.indexOf(peer.id) === -1) {
1641
+ connections.push(peer.id);
1642
+ this.peerMap.set(from, connections);
1643
+ }
1644
+ }
1645
+ const data = new PeerMessageResponse({id: this.id});
1646
+ const node = await this.prepareMessage(from, data.encoded);
1647
+
1648
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1649
+ } else if (proto.name === 'peernet-peer-response') {
1650
+ const from = proto.decoded.id;
1651
+ if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1652
+ else {
1653
+ const connections = this.peerMap.get(from);
1654
+ if (connections.indexOf(peer.id) === -1) {
1655
+ connections.push(peer.id);
1656
+ this.peerMap.set(from, connections);
1657
+ }
1658
+ }
1659
+ } else {
1660
+ let from = this._getPeerId(peer.id);
1661
+ if (!from) {
1662
+ const data = new PeerMessage({id: this.id});
1663
+ const node = await this.prepareMessage(peer.id, data.encoded);
1664
+
1665
+ let response = await peer.request(node.encoded);
1666
+ response = protoFor(response);
1667
+ response = new PeerMessageResponse(response.decoded.data);
1668
+
1669
+ from = response.decoded.id;
1670
+ if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1671
+ else {
1672
+ const connections = this.peerMap.get(from);
1673
+ if (connections.indexOf(peer.id) === -1) {
1674
+ connections.push(peer.id);
1675
+ this.peerMap.set(from, connections);
1676
+ }
1677
+ }
1678
+ }
1679
+ if (proto.name === 'peernet-dht') {
1680
+ let { hash, store } = proto.decoded;
1681
+ let has;
1682
+
1683
+ if (!store) {
1684
+ has = await this.has(hash);
1685
+ } else {
1686
+ store = globalThis[`${store}Store`];
1687
+ if (store.private) has = false;
1688
+ else has = await store.has(hash);
1689
+ }
1690
+ const data = new dhtResponse({hash, has});
1691
+ const node = await this.prepareMessage(from, data.encoded);
1692
+
1693
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1694
+ } else if (proto.name === 'peernet-data') {
1695
+ let { hash, store } = proto.decoded;
1696
+ let data;
1697
+
1698
+ if (!store) {
1699
+ data = await this.get(hash);
1700
+ } else {
1701
+ store = globalThis[`${store}Store`];
1702
+ if (store.private) {
1703
+ // TODO: ban
1704
+ return
1705
+ } else data = await store.get(hash);
1706
+ }
1707
+
1708
+ if (data) {
1709
+ data = new DataMessageResponse({hash, data: Buffer.from(data)});
1710
+
1711
+ const node = await this.prepareMessage(from, data.encoded);
1712
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1713
+ }
1714
+ } else if (proto.name === 'peernet-peer') {
1715
+ const from = proto.decoded.id;
1716
+ if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1717
+ else {
1718
+ const connections = this.peerMap.get(from);
1719
+ connections.push(peer.id);
1720
+ this.peerMap.set(from, connections);
1721
+ }
1722
+ const data = new PeerMessage({id: this.id});
1723
+ const node = await this.prepareMessage(from, data.encoded);
1724
+
1725
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1726
+ } else if (proto.name === 'peernet-request') {
1727
+ // TODO: make dynamic
1728
+ // exposeddevapi[proto.decoded.request](proto.decoded.params)
1729
+ const method = this.requestProtos[proto.decoded.request];
1730
+ if (method) {
1731
+ const data = await method();
1732
+ const node = await this.prepareMessage(from, data.encoded);
1733
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1734
+ }
1735
+ } else if (proto.name === 'peernet-ps' &&
1736
+ this._getPeerId(peer.id) !== this.id.toString()) {
1737
+ globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString());
1738
+ }
1739
+ }
1740
+ }
1741
+
1742
+ /**
1743
+ * performs a walk and resolves first encounter
1744
+ *
1745
+ * @param {String} hash
1746
+ */
1747
+ async walk(hash) {
1748
+ if (!hash) throw new Error('hash expected, received undefined')
1749
+ const data = new dht({hash});
1750
+ this.client.id;
1751
+ for (const peer of this.peers) {
1752
+ const node = await this.prepareMessage(peer.id, data.encoded);
1753
+
1754
+ const result = await peer.request(node.encoded);
1755
+
1756
+ let proto = protoFor(result.data);
1757
+
1758
+ if (proto.name !== 'peernet-message') throw encapsulatedError()
1759
+ const from = proto.decoded.from;
1760
+ proto = protoFor(proto.decoded.data);
1761
+
1762
+ if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
1763
+
1764
+ // TODO: give ip and port (just used for location)
1765
+ if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
1766
+ peer.connection.remoteFamily = 'ipv4';
1767
+ peer.connection.remoteAddress = '127.0.0.1';
1768
+ peer.connection.remotePort = '0000';
1769
+ }
1770
+
1771
+ const peerInfo = {
1772
+ family: peer.connection.remoteFamily || peer.connection.localFamily,
1773
+ address: peer.connection.remoteAddress || peer.connection.localAddress,
1774
+ port: peer.connection.remotePort || peer.connection.localPort,
1775
+ id: from,
1776
+ };
1777
+
1778
+ if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash);
1779
+ }
1780
+ return
1781
+ }
1782
+
1783
+ /**
1784
+ * Override DHT behavior, try's finding the content three times
1785
+ *
1786
+ * @param {String} hash
1787
+ */
1788
+ async providersFor(hash) {
1789
+ let providers = await this.dht.providersFor(hash);
1790
+ // walk the network to find a provider
1791
+ if (!providers || providers.length === 0) {
1792
+ await this.walk(hash);
1793
+ providers = await this.dht.providersFor(hash);
1794
+ // second walk the network to find a provider
1795
+ if (!providers || providers.length === 0) {
1796
+ await this.walk(hash);
1797
+ providers = await this.dht.providersFor(hash);
1798
+ }
1799
+ // last walk
1800
+ if (!providers || providers.length === 0) {
1801
+ await this.walk(hash);
1802
+ providers = await this.dht.providersFor(hash);
1803
+ }
1804
+ }
1805
+ // undefined if no providers given
1806
+ return providers
1807
+ }
1808
+
1809
+ get block() {
1810
+ return {
1811
+ get: async (hash) => {
1812
+ const data = await blockStore.has(hash);
1813
+ if (data) return await blockStore.get(hash)
1814
+ return this.requestData(hash)
1815
+ },
1816
+ put: async (hash, data) => {
1817
+ if (await blockStore.has(hash)) return
1818
+ return await blockStore.put(hash, data)
1819
+ },
1820
+ has: async (hash) => await blockStore.has(hash, 'block'),
1821
+ }
1822
+ }
1823
+
1824
+ get transaction() {
1825
+ return {
1826
+ get: async (hash) => {
1827
+ const data = await transactionStore.has(hash);
1828
+ if (data) return await transactionStore.get(hash)
1829
+ return this.requestData(hash, 'transaction')
1830
+ },
1831
+ put: async (hash, data) => {
1832
+ if (await transactionStore.has(hash)) return
1833
+ return await transactionStore.put(hash, data)
1834
+ },
1835
+ has: async (hash) => await transactionStore.has(hash),
1836
+ }
1837
+ }
1838
+
1839
+ async requestData(hash, store) {
1840
+ const providers = await this.providersFor(hash);
1841
+ if (!providers || providers.size === 0) throw nothingFoundError(hash)
1842
+ debug(`found ${providers.size} provider(s) for ${hash}`);
1843
+ // get closest peer on earth
1844
+ const closestPeer = await this.dht.closestPeer(providers);
1845
+ // get peer instance by id
1846
+ if (!closestPeer || !closestPeer.id) return this.requestData(hash, store)
1847
+
1848
+ const id = closestPeer.id.toString();
1849
+ if (this.peers) {
1850
+ let closest = this.peers.filter((peer) => {
1851
+ if (this._getPeerId(peer.id) === id) return peer
1852
+ });
1853
+
1854
+ let data = new DataMessage({hash, store});
1855
+
1856
+ const node = await this.prepareMessage(id, data.encoded);
1857
+ if (closest[0]) data = await closest[0].request(node.encoded);
1858
+ else {
1859
+ closest = this.peers.filter((peer) => {
1860
+ if (peer.id.toString() === id) return peer
1861
+ });
1862
+ if (closest[0]) data = await closest[0].request(node.encoded);
1863
+ }
1864
+ if (data.data) {
1865
+ let proto = protoFor(Buffer.from(data.data));
1866
+ proto = protoFor(proto.decoded.data);
1867
+ return proto.decoded.data
1868
+ }
1869
+
1870
+ // this.put(hash, proto.decoded.data)
1871
+ }
1872
+ return null
1873
+ }
1874
+
1875
+
1876
+ get message() {
1877
+ return {
1878
+ /**
1879
+ * Get content for given message hash
1880
+ *
1881
+ * @param {String} hash
1882
+ */
1883
+ get: async (hash) => {
1884
+ debug(`get message ${hash}`);
1885
+ const message = await messageStore.has(hash);
1886
+ if (message) return await messageStore.get(hash)
1887
+ return this.requestData(hash, 'message')
1888
+ },
1889
+ /**
1890
+ * put message content
1891
+ *
1892
+ * @param {String} hash
1893
+ * @param {Buffer} message
1894
+ */
1895
+ put: async (hash, message) => await messageStore.put(hash, message),
1896
+ /**
1897
+ * @param {String} hash
1898
+ * @return {Boolean}
1899
+ */
1900
+ has: async (hash) => await messageStore.has(hash),
1901
+ }
1902
+ }
1903
+
1904
+ get data() {
1905
+ return {
1906
+ /**
1907
+ * Get content for given data hash
1908
+ *
1909
+ * @param {String} hash
1910
+ */
1911
+ get: async (hash) => {
1912
+ debug(`get data ${hash}`);
1913
+ const data = await dataStore.has(hash);
1914
+ if (data) return await dataStore.get(hash)
1915
+ return this.requestData(hash, 'data')
1916
+ },
1917
+ /**
1918
+ * put data content
1919
+ *
1920
+ * @param {String} hash
1921
+ * @param {Buffer} data
1922
+ */
1923
+ put: async (hash, data) => await dataStore.put(hash, data),
1924
+ /**
1925
+ * @param {String} hash
1926
+ * @return {Boolean}
1927
+ */
1928
+ has: async (hash) => await dataStore.has(hash),
1929
+ }
1930
+ }
1931
+
1932
+ /**
1933
+ * goes trough given stores and tries to find data for given hash
1934
+ * @param {Array} stores
1935
+ * @param {string} hash
1936
+ */
1937
+ async whichStore(stores, hash) {
1938
+ let store = stores.pop();
1939
+ store = globalThis[`${store}Store`];
1940
+ if (store) {
1941
+ const has = await store.has(hash);
1942
+ if (has) return store
1943
+ if (stores.length > 0) return this.whichStore(stores, hash)
1944
+ } else return null
1945
+ }
1946
+
1947
+ /**
1948
+ * Get content for given hash
1949
+ *
1950
+ * @param {String} hash
1951
+ */
1952
+ async get(hash, store) {
1953
+ debug(`get ${hash}`);
1954
+ let data;
1955
+ if (store) store = globalThis[`${store}Store`];
1956
+ if (!store) store = await this.whichStore([...this.stores], hash);
1957
+ if (store && await store.has(hash)) data = await store.get(hash);
1958
+ if (data) return data
1959
+
1960
+ return this.requestData(hash, 'data')
1961
+ }
1962
+
1963
+ /**
1964
+ * put content
1965
+ *
1966
+ * @param {String} hash
1967
+ * @param {Buffer} data
1968
+ */
1969
+ async put(hash, data, store = 'data') {
1970
+ store = globalThis[`${store}Store`];
1971
+ return await store.put(hash, data)
1972
+ }
1973
+
1974
+ /**
1975
+ * @param {String} hash
1976
+ * @return {Boolean}
1977
+ */
1978
+ async has(hash) {
1979
+ const store = await this.whichStore([...this.stores], hash);
1980
+ if (store) {
1981
+ if (store.private) return false
1982
+ else return true
1983
+ }
1984
+ return false
1985
+ }
1986
+
1987
+ /**
1988
+ *
1989
+ * @param {String} topic
1990
+ * @param {String|Object|Array|Boolean|Buffer} data
1991
+ */
1992
+ async publish(topic, data) {
1993
+ // globalSub.publish(topic, data)
1994
+ if (!Buffer.isBuffer(topic)) topic = Buffer.from(topic);
1995
+ if (!Buffer.isBuffer(data)) data = Buffer.from(data);
1996
+ const id = Math.random().toString(36).slice(-12);
1997
+ data = new PsMessage({data, topic});
1998
+ for (const peer of this.peers) {
1999
+ if (peer.connection._connected) {
2000
+ if (peer.id.toString() !== this.peerId.toString()) {
2001
+ const node = await this.prepareMessage(peer.id, data.encoded);
2002
+ peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
2003
+ }
2004
+ } else {
2005
+ this.removePeer(peer);
2006
+ }
2007
+ // TODO: if peer subscribed
2008
+ }
2009
+ }
2010
+
2011
+ createHash(data, name) {
2012
+ return new hash(data, {name})
2013
+ }
2014
+
2015
+ /**
2016
+ *
2017
+ * @param {String} topic
2018
+ * @param {Method} cb
2019
+ */
2020
+ async subscribe(topic, cb) {
2021
+ // TODO: if peer subscribed
2022
+ globalSub.subscribe(topic, cb);
2023
+ }
2024
+
2025
+ async removePeer(peer) {
2026
+ connections.delete(peer.id);
2027
+ }
2028
+
2029
+ get Buffer() {
2030
+ return Buffer
2031
+ }
2032
+ // async block(index) {
2033
+ // const _values = []
2034
+ // for (const peer of this.peers) {
2035
+ // const value = await peer.request({type: 'block', index})
2036
+ // console.log(value);
2037
+ // }
2038
+ //
2039
+ // }
2044
2040
  }
2045
2041
 
2046
2042
  module.exports = Peernet;