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