@leofcoin/peernet 0.9.2 → 0.9.5

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.
@@ -14,7 +14,7 @@ var codecFormatInterface = require('./codec-format-interface.js');
14
14
  var request = require('./request.js');
15
15
  var response = require('./response.js');
16
16
  var fetch$1 = require('node-fetch');
17
- var codec = require('./codec.js');
17
+ var codec = require('./codec-73adfc0f.js');
18
18
  var hash = require('./hash.js');
19
19
  var MultiWallet = require('@leofcoin/multi-wallet');
20
20
  require('bs32');
@@ -264,7 +264,7 @@ class LeofcoinStorage$1 {
264
264
 
265
265
  }
266
266
 
267
- var version = "0.9.1";
267
+ var version = "0.9.4";
268
268
 
269
269
  var api$1 = {
270
270
  version: ({send}) => send({client: '@peernet/api/http', version}),
@@ -1036,7 +1036,7 @@ const debug = (log) => {
1036
1036
 
1037
1037
  const protoFor = (data) => {
1038
1038
  if (!Buffer.isBuffer(data)) data = Buffer.from(data);
1039
- const codec$1 = new codec(data);
1039
+ const codec$1 = new codec.PeernetCodec(data);
1040
1040
  if (!codec$1.name) throw new Error('proto not found')
1041
1041
  const Proto = globalThis.peernet.protos[codec$1.name];
1042
1042
  if (!Proto) throw (new Error(`No proto defined for ${codec$1.name}`))
@@ -1391,649 +1391,656 @@ const nothingFoundError = (hash) => {
1391
1391
  return new Error(`nothing found for ${hash}`)
1392
1392
  };
1393
1393
 
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 (!globalThis.peernet.protos[name]) globalThis.peernet.protos[name] = proto;
1439
- }
1440
-
1441
- addCodec(name, proto) {
1442
- if (!globalThis.peernet.codecs[name]) globalThis.peernet.codecs[name] = proto;
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.storePrefix = options.storePrefix;
1501
- this.root = options.root;
1502
-
1503
- /**
1504
- * proto Object containing protos
1505
- * @type {Object}
1506
- * @property {PeernetMessage} protos[peernet-message] messageNode
1507
- * @property {DHTMessage} protos[peernet-dht] messageNode
1508
- * @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
1509
- * @property {DataMessage} protos[peernet-data] messageNode
1510
- * @property {DataMessageResponse} protos[peernet-data-response] messageNode
1511
- */
1512
- globalThis.peernet.protos = {
1513
- 'peernet-request': request,
1514
- 'peernet-response': response,
1515
- 'peernet-peer': PeerMessage,
1516
- 'peernet-peer-response': PeerMessageResponse,
1517
- 'peernet-message': peernetMessage,
1518
- 'peernet-dht': dht,
1519
- 'peernet-dht-response': dhtResponse,
1520
- 'peernet-data': DataMessage,
1521
- 'peernet-data-response': DataMessageResponse,
1522
- 'peernet-ps': PsMessage,
1523
- 'chat-message': ChatMessage,
1524
- };
1525
- this.protos = globalThis.peernet.protos;
1526
-
1527
- this._messageHandler = new MessageHandler(this.network);
1528
-
1529
- const {daemon, environment} = await target();
1530
- this.hasDaemon = daemon;
1531
-
1532
- if (this.hasDaemon) {
1533
- globalThis.peernet.client = await httpClient({
1534
- protocol: 'peernet-v0.1.0', host: '127.0.0.1', port: options.port,
1535
- });
1536
- } else {
1537
- if (environment !== 'browser') http(options);
1538
- }
1539
-
1540
- for (const store of this.defaultStores) {
1541
- await this.addStore(store, options.storePrefix, options.root);
1542
- }
1543
-
1544
- try {
1545
- const pub = await accountStore.get('public');
1546
- this.id = pub.walletId;
1547
- } catch (e) {
1548
- if (e.code === 'ERR_NOT_FOUND') {
1549
- const wallet = {};
1550
- const {identity, accounts, config} = await generateAccount(this.network);
1551
- wallet.identity = identity;
1552
- wallet.accounts = accounts;
1553
- wallet.version = 1;
1554
- walletStore.put(wallet);
1555
- await accountStore.put('config', config);
1556
- await accountStore.put('public', {walletId: wallet.identity.walletId});
1557
-
1558
- this.id = wallet.identity.walletId;
1559
- } else {
1560
- throw e
1561
- }
1562
- }
1563
- this._peerHandler = new PeerDiscovery(this.id);
1564
- // peernet id
1565
- const id = Buffer.from(this.id.slice(0, 32));
1566
- this.peerId = id;
1567
-
1568
- pubsub.subscribe('peer:discovered', async (peer) => {
1569
- this._peerHandler.discover(peer);
1570
- peer.on('peernet.data', async (message) => {
1571
- const id = message.id;
1572
- message = new peernetMessage(Buffer.from(message.data.data));
1573
- const proto = protoFor(message.decoded.data);
1574
- await this._protoHandler({id, proto}, peer);
1575
- const fulldId = this._getPeerId(peer.id);
1576
- if (fulldId && this._discovered.indexOf(peer.id) === -1) {
1577
- this._discovered.push(peer.id);
1578
- pubsub.publish('peer:connected', peer);
1579
- }
1580
- });
1581
- });
1582
- pubsub.subscribe('peer:disconnected', async (peer) => {
1583
- let index = this._discovered.indexOf(peer.id);
1584
- if (index !== -1) this._discovered.splice(index, 1);
1585
- const id = this._getPeerId(peer.id);
1586
- let peerIds = this.peerMap.get(id);
1587
-
1588
- if (peerIds) {
1589
- index = peerIds.indexOf(peer.id);
1590
- if (index !== -1) peerIds.splice(index, 1);
1591
- } else {
1592
- peerIds = [];
1593
- }
1594
-
1595
- if (peerIds.length === 0) this.peerMap.delete(id);
1596
- else this.peerMap.set(id, peerIds);
1597
- });
1598
- pubsub.subscribe('peer:connected', async (peer) => {
1599
- console.log({connected: peer.id, as: this._getPeerId(peer.id) });
1600
- // peer.on('peernet.data', async (message) => {
1601
- // const id = message.id
1602
- // message = new PeernetMessage(Buffer.from(message.data.data))
1603
- // const proto = protoFor(message.decoded.data)
1604
- // this._protoHandler({id, proto}, peer)
1605
- // })
1606
- });
1607
-
1608
- /**
1609
- * @access public
1610
- * @type {PeernetClient}
1611
- */
1612
- this.client = new PeernetClient({...options, id});
1613
- return this
1614
- }
1615
-
1616
- _getPeerId(id) {
1617
- for (const entry of [...this.peerMap.entries()]) {
1618
- for (const _id of entry[1]) {
1619
- if (_id === id) return entry[0]
1620
- }
1621
- }
1622
- }
1623
-
1624
- /**
1625
- * @private
1626
- *
1627
- * @param {Buffer} message - peernet message
1628
- * @param {PeernetPeer} peer - peernet peer
1629
- */
1630
- async _protoHandler(message, peer) {
1631
- const {id, proto} = message;
1632
- if (proto.name === 'peernet-peer') {
1633
- const from = proto.decoded.id;
1634
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1635
- else {
1636
- const connections = this.peerMap.get(from);
1637
- if (connections.indexOf(peer.id) === -1) {
1638
- connections.push(peer.id);
1639
- this.peerMap.set(from, connections);
1640
- }
1641
- }
1642
- const data = new PeerMessageResponse({id: this.id});
1643
- const node = await this.prepareMessage(from, data.encoded);
1644
-
1645
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1646
- } else if (proto.name === 'peernet-peer-response') {
1647
- const from = proto.decoded.id;
1648
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1649
- else {
1650
- const connections = this.peerMap.get(from);
1651
- if (connections.indexOf(peer.id) === -1) {
1652
- connections.push(peer.id);
1653
- this.peerMap.set(from, connections);
1654
- }
1655
- }
1656
- } else {
1657
- let from = this._getPeerId(peer.id);
1658
- if (!from) {
1659
- const data = new PeerMessage({id: this.id});
1660
- const node = await this.prepareMessage(peer.id, data.encoded);
1661
-
1662
- let response = await peer.request(node.encoded);
1663
- response = protoFor(response);
1664
- response = new PeerMessageResponse(response.decoded.data);
1665
-
1666
- from = response.decoded.id;
1667
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1668
- else {
1669
- const connections = this.peerMap.get(from);
1670
- if (connections.indexOf(peer.id) === -1) {
1671
- connections.push(peer.id);
1672
- this.peerMap.set(from, connections);
1673
- }
1674
- }
1675
- }
1676
- if (proto.name === 'peernet-dht') {
1677
- let { hash, store } = proto.decoded;
1678
- let has;
1679
-
1680
- if (!store) {
1681
- has = await this.has(hash);
1682
- } else {
1683
- store = globalThis[`${store}Store`];
1684
- if (store.private) has = false;
1685
- else has = await store.has(hash);
1686
- }
1687
- const data = new dhtResponse({hash, has});
1688
- const node = await this.prepareMessage(from, data.encoded);
1689
-
1690
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1691
- } else if (proto.name === 'peernet-data') {
1692
- let { hash, store } = proto.decoded;
1693
- let data;
1694
-
1695
- if (!store) {
1696
- data = await this.get(hash);
1697
- } else {
1698
- store = globalThis[`${store}Store`];
1699
- if (store.private) {
1700
- // TODO: ban
1701
- return
1702
- } else data = await store.get(hash);
1703
- }
1704
-
1705
- if (data) {
1706
- data = new DataMessageResponse({hash, data: Buffer.from(data)});
1707
-
1708
- const node = await this.prepareMessage(from, data.encoded);
1709
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1710
- }
1711
- } else if (proto.name === 'peernet-peer') {
1712
- const from = proto.decoded.id;
1713
- if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
1714
- else {
1715
- const connections = this.peerMap.get(from);
1716
- connections.push(peer.id);
1717
- this.peerMap.set(from, connections);
1718
- }
1719
- const data = new PeerMessage({id: this.id});
1720
- const node = await this.prepareMessage(from, data.encoded);
1721
-
1722
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1723
- } else if (proto.name === 'peernet-request') {
1724
- // TODO: make dynamic
1725
- // exposeddevapi[proto.decoded.request](proto.decoded.params)
1726
- let response$1;
1727
- if (proto.decoded.request === 'lastBlock') {
1728
- const height = await chainStore.get('localIndex');
1729
- const hash = await chainStore.get('localBlock');
1730
- response$1 = JSON.stringify({ height: height.toString(), hash: hash.toString() });
1731
- const data = new response({ response: response$1 });
1732
- const node = await this.prepareMessage(from, data.encoded);
1733
-
1734
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
1735
- }
1736
- } else if (proto.name === 'peernet-ps' &&
1737
- this._getPeerId(peer.id) !== this.id.toString()) {
1738
- globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString());
1739
- }
1740
- }
1741
- }
1742
-
1743
- /**
1744
- * performs a walk and resolves first encounter
1745
- *
1746
- * @param {String} hash
1747
- */
1748
- async walk(hash) {
1749
- if (!hash) throw new Error('hash expected, received undefined')
1750
- const data = new dht({hash});
1751
- this.client.id;
1752
- for (const peer of this.peers) {
1753
- const node = await this.prepareMessage(peer.id, data.encoded);
1754
-
1755
- const result = await peer.request(node.encoded);
1756
-
1757
- let proto = protoFor(result.data);
1758
-
1759
- if (proto.name !== 'peernet-message') throw encapsulatedError()
1760
- const from = proto.decoded.from;
1761
- proto = protoFor(proto.decoded.data);
1762
-
1763
- if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
1764
-
1765
- // TODO: give ip and port (just used for location)
1766
- if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
1767
- peer.connection.remoteFamily = 'ipv4';
1768
- peer.connection.remoteAddress = '127.0.0.1';
1769
- peer.connection.remotePort = '0000';
1770
- }
1771
-
1772
- const peerInfo = {
1773
- family: peer.connection.remoteFamily || peer.connection.localFamily,
1774
- address: peer.connection.remoteAddress || peer.connection.localAddress,
1775
- port: peer.connection.remotePort || peer.connection.localPort,
1776
- id: from,
1777
- };
1778
-
1779
- if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash);
1780
- }
1781
- return
1782
- }
1783
-
1784
- /**
1785
- * Override DHT behavior, try's finding the content three times
1786
- *
1787
- * @param {String} hash
1788
- */
1789
- async providersFor(hash) {
1790
- let providers = await this.dht.providersFor(hash);
1791
- // walk the network to find a provider
1792
- if (!providers || providers.length === 0) {
1793
- await this.walk(hash);
1794
- providers = await this.dht.providersFor(hash);
1795
- // second walk the network to find a provider
1796
- if (!providers || providers.length === 0) {
1797
- await this.walk(hash);
1798
- providers = await this.dht.providersFor(hash);
1799
- }
1800
- // last walk
1801
- if (!providers || providers.length === 0) {
1802
- await this.walk(hash);
1803
- providers = await this.dht.providersFor(hash);
1804
- }
1805
- }
1806
- // undefined if no providers given
1807
- return providers
1808
- }
1809
-
1810
- get block() {
1811
- return {
1812
- get: async (hash) => {
1813
- const data = await blockStore.has(hash);
1814
- if (data) return await blockStore.get(hash)
1815
- return this.requestData(hash)
1816
- },
1817
- put: async (hash, data) => {
1818
- if (await blockStore.has(hash)) return
1819
- return await blockStore.put(hash, data)
1820
- },
1821
- has: async (hash) => await blockStore.has(hash, 'block'),
1822
- }
1823
- }
1824
-
1825
- get transaction() {
1826
- return {
1827
- get: async (hash) => {
1828
- const data = await transactionStore.has(hash);
1829
- if (data) return await transactionStore.get(hash)
1830
- return this.requestData(hash, 'transaction')
1831
- },
1832
- put: async (hash, data) => {
1833
- if (await transactionStore.has(hash)) return
1834
- return await transactionStore.put(hash, data)
1835
- },
1836
- has: async (hash) => await transactionStore.has(hash),
1837
- }
1838
- }
1839
-
1840
- async requestData(hash, store) {
1841
- const providers = await this.providersFor(hash);
1842
- if (!providers || providers.size === 0) throw nothingFoundError(hash)
1843
- debug(`found ${providers.size} provider(s) for ${hash}`);
1844
- // get closest peer on earth
1845
- const closestPeer = await this.dht.closestPeer(providers);
1846
- // get peer instance by id
1847
- if (!closestPeer || !closestPeer.id) return this.requestData(hash, store)
1848
-
1849
- const id = closestPeer.id.toString();
1850
- if (this.peers) {
1851
- let closest = this.peers.filter((peer) => {
1852
- if (this._getPeerId(peer.id) === id) return peer
1853
- });
1854
-
1855
- let data = new DataMessage({hash, store});
1856
-
1857
- const node = await this.prepareMessage(id, data.encoded);
1858
- if (closest[0]) data = await closest[0].request(node.encoded);
1859
- else {
1860
- closest = this.peers.filter((peer) => {
1861
- if (peer.id.toString() === id) return peer
1862
- });
1863
- if (closest[0]) data = await closest[0].request(node.encoded);
1864
- }
1865
- if (data.data) {
1866
- let proto = protoFor(Buffer.from(data.data));
1867
- proto = protoFor(proto.decoded.data);
1868
- return proto.decoded.data
1869
- }
1870
-
1871
- // this.put(hash, proto.decoded.data)
1872
- }
1873
- return null
1874
- }
1875
-
1876
-
1877
- get message() {
1878
- return {
1879
- /**
1880
- * Get content for given message hash
1881
- *
1882
- * @param {String} hash
1883
- */
1884
- get: async (hash) => {
1885
- debug(`get message ${hash}`);
1886
- const message = await messageStore.has(hash);
1887
- if (message) return await messageStore.get(hash)
1888
- return this.requestData(hash, 'message')
1889
- },
1890
- /**
1891
- * put message content
1892
- *
1893
- * @param {String} hash
1894
- * @param {Buffer} message
1895
- */
1896
- put: async (hash, message) => await messageStore.put(hash, message),
1897
- /**
1898
- * @param {String} hash
1899
- * @return {Boolean}
1900
- */
1901
- has: async (hash) => await messageStore.has(hash),
1902
- }
1903
- }
1904
-
1905
- get data() {
1906
- return {
1907
- /**
1908
- * Get content for given data hash
1909
- *
1910
- * @param {String} hash
1911
- */
1912
- get: async (hash) => {
1913
- debug(`get data ${hash}`);
1914
- const data = await dataStore.has(hash);
1915
- if (data) return await dataStore.get(hash)
1916
- return this.requestData(hash, 'data')
1917
- },
1918
- /**
1919
- * put data content
1920
- *
1921
- * @param {String} hash
1922
- * @param {Buffer} data
1923
- */
1924
- put: async (hash, data) => await dataStore.put(hash, data),
1925
- /**
1926
- * @param {String} hash
1927
- * @return {Boolean}
1928
- */
1929
- has: async (hash) => await dataStore.has(hash),
1930
- }
1931
- }
1932
-
1933
- /**
1934
- * goes trough given stores and tries to find data for given hash
1935
- * @param {Array} stores
1936
- * @param {string} hash
1937
- */
1938
- async whichStore(stores, hash) {
1939
- let store = stores.pop();
1940
- store = globalThis[`${store}Store`];
1941
- if (store) {
1942
- const has = await store.has(hash);
1943
- if (has) return store
1944
- if (stores.length > 0) return this.whichStore(stores, hash)
1945
- } else return null
1946
- }
1947
-
1948
- /**
1949
- * Get content for given hash
1950
- *
1951
- * @param {String} hash
1952
- */
1953
- async get(hash, store) {
1954
- debug(`get ${hash}`);
1955
- let data;
1956
- if (store) store = globalThis[`${store}Store`];
1957
- if (!store) store = await this.whichStore([...this.stores], hash);
1958
- if (store && await store.has(hash)) data = await store.get(hash);
1959
- if (data) return data
1960
-
1961
- return this.requestData(hash, 'data')
1962
- }
1963
-
1964
- /**
1965
- * put content
1966
- *
1967
- * @param {String} hash
1968
- * @param {Buffer} data
1969
- */
1970
- async put(hash, data, store = 'data') {
1971
- store = globalThis[`${store}Store`];
1972
- return await store.put(hash, data)
1973
- }
1974
-
1975
- /**
1976
- * @param {String} hash
1977
- * @return {Boolean}
1978
- */
1979
- async has(hash) {
1980
- const store = await this.whichStore([...this.stores], hash);
1981
- if (store) {
1982
- if (store.private) return false
1983
- else return true
1984
- }
1985
- return false
1986
- }
1987
-
1988
- /**
1989
- *
1990
- * @param {String} topic
1991
- * @param {String|Object|Array|Boolean|Buffer} data
1992
- */
1993
- async publish(topic, data) {
1994
- // globalSub.publish(topic, data)
1995
- if (!Buffer.isBuffer(topic)) topic = Buffer.from(topic);
1996
- if (!Buffer.isBuffer(data)) data = Buffer.from(data);
1997
- const id = Math.random().toString(36).slice(-12);
1998
- data = new PsMessage({data, topic});
1999
- for (const peer of this.peers) {
2000
- if (peer.connection._connected) {
2001
- if (peer.id.toString() !== this.peerId.toString()) {
2002
- const node = await this.prepareMessage(peer.id, data.encoded);
2003
- peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
2004
- }
2005
- } else {
2006
- this.removePeer(peer);
2007
- }
2008
- // TODO: if peer subscribed
2009
- }
2010
- }
2011
-
2012
- createHash(data, name) {
2013
- return new hash(data, {name})
2014
- }
2015
-
2016
- /**
2017
- *
2018
- * @param {String} topic
2019
- * @param {Method} cb
2020
- */
2021
- async subscribe(topic, cb) {
2022
- // TODO: if peer subscribed
2023
- globalSub.subscribe(topic, cb);
2024
- }
2025
-
2026
- async removePeer(peer) {
2027
- connections.delete(peer.id);
2028
- }
2029
- // async block(index) {
2030
- // const _values = []
2031
- // for (const peer of this.peers) {
2032
- // const value = await peer.request({type: 'block', index})
2033
- // console.log(value);
2034
- // }
2035
- //
2036
- // }
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
+ // }
2037
2044
  }
2038
2045
 
2039
2046
  module.exports = Peernet;