@leofcoin/peernet 0.9.11 → 0.9.12

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