helia-coord 1.2.25 → 1.4.0

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.
Files changed (46) hide show
  1. package/README.md +2 -0
  2. package/config/global-config.js +5 -1
  3. package/dev-docs/peers.md +0 -0
  4. package/dev-docs/pubsub.md +3 -0
  5. package/dev-docs/relays.md +0 -0
  6. package/dev-docs/startup.md +6 -0
  7. package/dev-docs/temp.md +62 -0
  8. package/dev-docs/theory-of-operation.md +21 -2
  9. package/dev-docs/this-node.md +0 -0
  10. package/dev-docs/usage-and-code.md +115 -0
  11. package/examples/create-helia-node.js +154 -0
  12. package/examples/start-node.js +34 -0
  13. package/index.js +10 -8
  14. package/lib/adapters/ipfs-adapter.js +3 -0
  15. package/lib/adapters/pubsub-adapter/index.js +34 -4
  16. package/lib/controllers/index.js +8 -0
  17. package/lib/controllers/pubsub-controller.js +69 -0
  18. package/lib/controllers/timer-controller.js +31 -2
  19. package/lib/entities/peers.js +3 -0
  20. package/lib/entities/pubsub.js +23 -0
  21. package/lib/entities/relays.js +3 -0
  22. package/lib/entities/this-node-entity.js +2 -40
  23. package/lib/use-cases/index.js +4 -1
  24. package/lib/use-cases/peer-use-cases.js +500 -0
  25. package/lib/use-cases/pubsub-use-cases.js +186 -13
  26. package/lib/use-cases/relay-use-cases.js +46 -62
  27. package/lib/use-cases/this-node-use-cases.js +3 -347
  28. package/package.json +13 -4
  29. package/test/mocks/adapter-mock.js +5 -2
  30. package/test/mocks/peers-mock.js +9 -2
  31. package/test/mocks/use-case-mocks.js +7 -2
  32. package/test/unit/adapters/gist.unit.adapters.js +18 -18
  33. package/test/unit/adapters/pubsub/pubsub-adapter-unit.js +16 -28
  34. package/test/unit/controllers/controllers-index-unit.js +15 -0
  35. package/test/unit/controllers/pubsub-controller.unit.js +107 -0
  36. package/test/unit/controllers/timer-controller-unit.js +21 -5
  37. package/test/unit/index-unit.js +2 -4
  38. package/test/unit/use-cases/peer.unit.use-cases.js +562 -1
  39. package/test/unit/use-cases/pubsub.unit.use-cases.js +168 -9
  40. package/test/unit/use-cases/relay-use-cases-unit.js +119 -64
  41. package/test/unit/use-cases/this-node-use-cases-unit.js +8 -358
  42. package/test/unit/use-cases/use-cases-index-unit.js +4 -1
  43. package/config/bootstrap-circuit-relays.js +0 -48
  44. package/examples/start-external.js +0 -56
  45. package/lib/adapters/pubsub-adapter/about-adapter.js +0 -117
  46. package/test/unit/adapters/pubsub/about-adapter-unit.js +0 -129
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
1
  # helia-coord
2
2
 
3
3
  This is a fork of [ipfs-coord-esm](https://github.com/Permissionless-Software-Foundation/ipfs-coord-esm), adpted for use with [Helia](https://github.com/ipfs/helia) instead of Kubo.
4
+
5
+ Read the [dev-docs](./dev-docs) for more information.
@@ -7,7 +7,11 @@ const config = {
7
7
  BCH_COINJOIN_ROOM: 'bch-coinjoin-001',
8
8
 
9
9
  // Time between retrying private messages to a peer.
10
- TIME_BETWEEN_RETRIES: 5000
10
+ TIME_BETWEEN_RETRIES: 5000,
11
+
12
+ // Maximum amount of time in milliseconds to wait for a peer to respond to
13
+ // an /about query, which is used to measure latency between peers.
14
+ MAX_LATENCY: 20000
11
15
  }
12
16
 
13
17
  export default config
File without changes
@@ -0,0 +1,3 @@
1
+ # Pubsub
2
+
3
+ One of the primary functions of the helia-coord library is
File without changes
@@ -0,0 +1,6 @@
1
+ # Startup
2
+
3
+ Outline of startup procedure:
4
+
5
+ - Instantiate the library by passing in a configuration object.
6
+ -
@@ -0,0 +1,62 @@
1
+ This markdown is for holding temporary piece of text for editing.
2
+
3
+ ## Use cases
4
+
5
+ ### Peers
6
+
7
+ Peers are other IPFS nodes that the application wants to keep track of. These are peers that make up the subnet.
8
+
9
+ - `peerList` - An array of IPFS IDs (strings), identifying each peer this node knows about.
10
+ - `peerData` - An object with root properties that match the peer IPFS ID. Each root property represents a peer and contains the data about that peer.
11
+
12
+ ### Pubsub Channels
13
+
14
+ [Pubsub Channels](https://blog.ipfs.io/29-js-ipfs-pubsub/) are the way that IPFS nodes form a subnet. The members of the subnet are all subscribed to the same pubsub channel.
15
+
16
+ ### Relays
17
+
18
+ Some nodes using ipfs-coord can elect to become [Circuit Relays](https://docs.libp2p.io/concepts/circuit-relay/). Circuit Relays are critical for keeping the network censorship resistant. They allow nodes that otherwise would not be able to communicate with one another, do so. They assist in punching through network firewalls that would otherwise block communication. They allow the subnet to route around damage and dynamically adjust as nodes enter and leave the subnet.
19
+
20
+ ipfs-coord will start by connecting to a small set of pre-configured Relays. As it discovers new peers in the subnetwork that have their `isCircuitRelay` flag set, it will expand its connections to as many Relays as it can find.
21
+
22
+ - `relayList` - An array of IPFS IDs (strings), identifying each Relay this node knows about.
23
+ - `relayData` - An object with root properties that match the relay IPFS ID. Each root property represents a relay and contains the data about that relay.
24
+
25
+ ### Services
26
+
27
+ Some nodes are 'service providers' while other nodes are 'service consumers'. These [Decentralized Service Providers (Video)](https://youtu.be/m_33rRXEats) can provide traditional 'back end' web services while leveraging the censorship resistance and automatic networking of this library. Apps consuming these services can use this library can track different service providers, to dynamically load the services it needs to function.
28
+
29
+ - `serviceList` - An array of IPFS IDs (strings), identifying each Service this node knows about.
30
+ - `serviceData` - An object with root properties that match the Services IPFS ID. Each root property represents a Service and contains the data about that Service.
31
+
32
+ Properties maintained for each Service:
33
+
34
+ - jwtFee - If the Service requires the purchase of a JWT token to access it, this is the cost of purchasing one. This is an array of objects, with each object describing different fee options. For example, there may be one fee for paying in BCH. There may be another for paying in SLP tokens.
35
+ - jwtDuration - The duration a JWT token lasts before expiring. A number representing hours.
36
+ - `protocol` - The service protocol that this Service offers.
37
+ - `version` - The protocol version that this Service offers.
38
+ - `description` - A brief description of the Service.
39
+ - `documentation` - An optional link to any API documentation needed to consume the Service.
40
+
41
+ ## Controllers
42
+
43
+ ### Pubsub
44
+
45
+ New messages arriving for a pubsub channel will trigger an event that will cause this library to process and route the message to the appropriate handler. A few classes of message:
46
+
47
+ - Announcement - Announcement messages from other peers will be routed to the Peer Entity. If its a new peer, it will be added to the list of known peers.
48
+
49
+ - Services - A peer advertising specific services will be passed on to the Services Entity.
50
+ - Relays - A peer advertising Circuit Relay capability will be passed on to the Relays Entity.
51
+
52
+ - Private Messages - Each peer has a pubsub channel that is the same name as its IPFS ID. Messages arriving on this channel are expected to be e2e encrypted with the peers public key. Any unencrypted messages are ignored.
53
+
54
+ ### Timers
55
+
56
+ - Announce Self - This controller announces the presence of the IPFS node by publishing a message to the general coordination pubsub channel. If it's a service provider, it will also announce itself in the service-specific coordination channel.
57
+
58
+ - Update Peers - This controller reviews the data about each known peer, and prunes away any peers that have not announced themselves for a period of time. It attempts to renew connections to each peer in the list.
59
+
60
+ - Update Relays - This controller reviews the data about each known Circuit Relay. It attempts to renew the connection to each known Circuit Relay.
61
+
62
+ - Update Services - This controller reviews the data about each known Service Provider. It prunes any services that it has not been able to connect to over a period of time.
@@ -1,7 +1,26 @@
1
1
  # Theory of Operation
2
2
 
3
- This document provides a high-level overview of what the helia-coord library does.
4
- Helia-coord is middleware that controls a Helia IPFS node. It is used to form an on-the-fly, self-healing mesh network of other IPFS nodes. The nodes communicate over *pubsub channels*, and state is managed by *interval timers*.
3
+ This document provides a high-level overview of the helia-coord library.
4
+ Helia-coord is middleware that controls a Helia IPFS node. It is used to form an on-the-fly, self-healing mesh network of other IPFS nodes. It tracks its connection to other nodes on the network with *entities*. The nodes communicate over *[pubsub channels](https://docs.libp2p.io/concepts/pubsub/overview/)*, and state is managed by *interval timers*.
5
+
6
+ ## Entities
7
+
8
+ There are three main entities tracked by helia-coord:
9
+ - *thisNode* - represents the IPFS node controlled by helia-coord.
10
+ - *peers* - are other IPFS peers on the network tracked by helia-coord.
11
+ - *relays* - are special *peers* that can establish a webRTC [Circuit Relay](https://docs.libp2p.io/concepts/nat/circuit-relay/) connection between nodes that can not talk to one another directly.
12
+ - *pubsub* channels - are communication channels that nodes subscribe to in order to communicate.
13
+
14
+ These entities are all stateless, meaning that that the node starts by knowing nothing about itself or the other peers on the network. The entities are created at run-time. They are created and information is added to them as the node discovers more about itself and the other peers on the network over time.
15
+
16
+ ### thisNode
17
+ The *thisNode* entity represents the Helia IPFS node controlled by helia-coord. There is only *one* instance of *thisNode*.
18
+
19
+ ### peers
20
+ A peer entity represents other IPFS nodes on the network that are also running the helia-coord library. These are entities that the *thisNode* entity wants to track and maintain connections to.
21
+
22
+ ### relays
23
+ Relay entities are peers, but not all peers are relays. Relays are a special peers with a public IP address, and run the [v2 Circuit Relay protocol](https://docs.libp2p.io/concepts/nat/circuit-relay/). *peer* entities that are behind firewalls and can not connect to one another directly, can connect through a *relay*.
5
24
 
6
25
  ## Pubsub Channels
7
26
 
File without changes
@@ -0,0 +1,115 @@
1
+ # ipfs-coord Specifications
2
+
3
+ ## Overview
4
+
5
+ ipfs-coord is a shortening of the word 'coordination'. It is a JavaScript npm library that helps applications using [js-ipfs](https://github.com/ipfs/js-ipfs) coordinate with other peers running related applications.
6
+
7
+ This document contains a high-level, human-readable specification for the four major architectural areas of the ipfs-coord library:
8
+
9
+ - Entities
10
+ - Use Cases
11
+ - Controllers (inputs)
12
+ - Adapters (outputs)
13
+
14
+ This reflects the [Clean Architecture](https://troutsblog.com/blog/clean-architecture) design pattern.
15
+
16
+ After the ipfs-coord library is instantiated, it will have properties `useCases`, `controllers`, and `adapters` that have methods corresponding to the descriptions in this document. Apps can exercise the features of the ipfs-coord library through this object-oriented structure.
17
+
18
+ ## Configuration
19
+
20
+ When instantiating the ipfs-coord library, the following configuration inputs can be passed to its constructor via an object with the properties indicated below. Be sure to check out the [examples directory](../examples) for examples on how to instantiate the library with different configurations.
21
+
22
+ - `ipfs`: (required) An instance of [js-ipfs](https://www.npmjs.com/package/ipfs). IPFS must be instantiated outside of ipfs-coord and passed into it when instantiating the ipfs-coord library.
23
+ - `bchjs`: (required) An instance of [bch-js](https://www.npmjs.com/package/@psf/bch-js). bch-js must be instantiated outside of ipfs-coord and passed into it when instantiating the ipfs-coord library.
24
+ - `type`: (required) A string with the value of 'browser' or 'node.js', to indicate what type of app is instantiating the library. This will determine the types of Circuit Relays the library can connect to.
25
+ - `statusLog`: A function for handling status output strings on the status of ipfs-coord. This defaults to `console.log` if not specified.
26
+ - `privateLog`: A function for handling private messages passed to this node from peer nodes. This defaults to `console.log` if not specified.
27
+
28
+ ## Entities
29
+
30
+ Entities make up the core business concepts. If these entities change, they fundamentally change the entire app.
31
+
32
+ ### thisNode
33
+
34
+ `thisNode` is the IPFS node consuming the ipfs-coord library. The thisNode Entity creates a representation the 'self' and maintains the state of the IPFS node, BCH wallet, peers, relays, and pubsub channels that the node is tracking.
35
+
36
+ ## Use Cases
37
+
38
+ Use cases are verbs or actions that is done _to_ an Entity or _between_ Entities.
39
+
40
+ ### thisNode
41
+
42
+ The `this-node-use-cases.js` library contains the following Use Cases:
43
+
44
+ - `createSelf()` - initializes the `thisNode` Entity. It takes the following actions:
45
+
46
+ - It retrieves basic information about the IPFS node like the ID and multiaddresses.
47
+ - It creates a BCH wallet and generates addresses for payments and a public key used for end-to-end encryption (e2ee).
48
+ - It creates an OrbitDB used to receive private e2ee messages.
49
+ - It initializes the Schema library for passing standardized messages.
50
+
51
+ - `addSubnetPeer()` - This is an event handler that is triggered when an 'announcement object' is recieved on the general coordination pubsub channel. That object is passed to `addSubnetPeer()` to be processed. It will analyze the announcement object and add the peer to the array of peers tracked by the thisNode Entity. If the peer is already known, its data will be updated.
52
+
53
+ - `refreshPeerConnections()` - is periodically called by the Timer Controller. It checks to see if thisNode is still connected to all the subnet peers. It will refresh the connection if they have been disconnected. Circuit Relays are used to connect to other subnet peers, and each known circuit relay will be cycled through until a connection can be established between thisNode and the subnet peer.
54
+
55
+ ### Relays
56
+
57
+ The `relay-use-cases.js` library controls the interactions between thisNode and the Circuit Relays that it knows about.
58
+
59
+ - `initializeRelays()` - The ipfs-coord library comes with a pre-programmed list of Circuit Relay nodes. This list is stored in `config/bootstrap-circuit-relays.js`. The `initializeRelays()` method is called once at startup to connect to these relays. This is what 'bootstraps' thisNode to the IPFS sub-network and allows it to find subnetwork peers. After that initial bootstrap connection, thisNode will automatically learn about and connect to other peers and circuit relays.
60
+
61
+ - `connectToCRs()` - This method is called periodically by the Timer Controller. It checks the connection between thisNode and each Circuit Relay node. If thisNode has lost its connection, the connection is restored.
62
+
63
+ ### Pubsub
64
+
65
+ The `pubsub-use-cases.js` has a single method:
66
+
67
+ - `initializePubsub()` is called at startup to connect the node to the general coordination pubsub channel. This is the channel where other apps running the ipfs-coord library announce themselves to other peers in the subnet.
68
+
69
+ ## Controllers
70
+
71
+ Controllers are inputs to the system. When a controller is activated, it causes the system to react in some way.
72
+
73
+ ### Timers
74
+
75
+ The controllers listed in this section are activated periodically by a timer. They do routine maintenance.
76
+
77
+ - `startTimers()` is called at startup. It initializes the other timer controllers.
78
+
79
+ - `manageCircuitRelays()` calls the `connectToCRs()` Use Case to refresh connections to other circuit relays.
80
+
81
+ - `manageAnnouncement()` periodically announces the presence of thisNode Entity on the general coordination pubsub channel. It allows other subnet peers to find the node.
82
+
83
+ - `managePeers()` checks the list of known subnet peers tracked by thisNode Entity. It will restore the connection to each peer if they get disconnected.
84
+
85
+ ## Adapters
86
+
87
+ Adapters are the 'outputs' of the system. They are the interfaces that this library manipulates in order to maintain the state of the Entities. Adapters ensure that the business logic doesn't need to know any specific information about the outputs.
88
+
89
+ ### bch-adapter.js
90
+
91
+ [bch-js](https://github.com/Permissionless-Software-Foundation/bch-js) is the Bitcoin Cash (BCH) library used to handle payments and end-to-end encryption in peer communication. When the IPFS node is started, it generates a BCH address to receive payments in BCH, and an SLP address to receive payments in SLP tokens. The same private key used to generate these addresses is used to decrypt incoming pubsub messages, and the public key is passed on to other peers so that they can encrypt messages they want to send thisNode.
92
+
93
+ ### ipfs-adapter.js
94
+
95
+ This library is designed primarily to control an IPFS node. However, it does not load IPFS directly. It expects the developer to inject an instance of js-ipfs when instantiating this library.
96
+
97
+ ### orbitdb-adapter.js
98
+
99
+ [OrbitDB](https://orbitdb.org/) is a database that runs on top of IPFS. It's used in this library to prevent 'dropped calls'. As nodes are constantly adjusting their network connections, they can sometimes miss pubsub messages. Other peers in the subnet maintain short-lived logs of the encrypted messages directed at the peers they are connected to. This allows them to pass the message on when the peer reconnects to them, preventing 'dropped calls'. These logs are abandoned after an hour and a new log is created, to prevent them from growing too large.
100
+
101
+ ### encryption-adapter.js
102
+
103
+ The encryption adapter is responsible for encryption and decryption of pubsub messages. It uses the same Eliptic Curve cryptography used by the Bitcoin protocol. The same private key that is used to generate the BCH address assigned to thisNode is the same private key used to decrypt incoming messages.
104
+
105
+ Other subnet peers that thisNode tracks will pass on their public key. All messages sent between nodes is encrypted with the receivers public key. Any unencrypted messages are ignored.
106
+
107
+ ### pubsub-adapter.js
108
+
109
+ The pubsub adapter can publish a message to a pubsub channel, and route incoming messages to an appropriate handler. There are (public) coordination channels that many peers subscribe to, and messages are published unencrypted.
110
+
111
+ Private messages between peers are _not_ controlled by this library. Those messages are published to OrbitDB and use pubsub messages indirectly, rather than being directly published to a pubsub channel by this library.
112
+
113
+ ### schema.js
114
+
115
+ The schema library contain formatted JSON objects that are used to generate a standardized messages for communication between peers. The schema.js library is instantiated at startup and appended to the thisNode Entity.
@@ -0,0 +1,154 @@
1
+ /*
2
+ This library creates a Helia IPFS node. This is done prior to attaching
3
+ helia-coord to the node.
4
+ This library is called by start-node.js.
5
+ */
6
+
7
+ // Global npm libraries
8
+ import { createHelia } from 'helia'
9
+ import fs from 'fs'
10
+ import { FsBlockstore } from 'blockstore-fs'
11
+ import { FsDatastore } from 'datastore-fs'
12
+ import { createLibp2p } from 'libp2p'
13
+ import { tcp } from '@libp2p/tcp'
14
+ import { noise } from '@chainsafe/libp2p-noise'
15
+ import { yamux } from '@chainsafe/libp2p-yamux'
16
+ // import { bootstrap } from '@libp2p/bootstrap'
17
+ import { identifyService } from 'libp2p/identify'
18
+ // import { identify } from '@libp2p/identify'
19
+ import { circuitRelayTransport } from 'libp2p/circuit-relay'
20
+ // import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2'
21
+ import { gossipsub } from '@chainsafe/libp2p-gossipsub'
22
+ import { webSockets } from '@libp2p/websockets'
23
+ import { publicIpv4 } from 'public-ip'
24
+ import { multiaddr } from '@multiformats/multiaddr'
25
+ import { webRTC } from '@libp2p/webrtc'
26
+
27
+ const ROOT_DIR = './'
28
+ const IPFS_DIR = './.ipfsdata/ipfs'
29
+
30
+ class CreateHeliaNode {
31
+ constructor () {
32
+ this.publicIp = publicIpv4
33
+ }
34
+
35
+ // Start an IPFS node.
36
+ async start () {
37
+ try {
38
+ // Ensure the directory structure exists that is needed by the IPFS node to store data.
39
+ this.ensureBlocksDir()
40
+
41
+ // Create an IPFS node
42
+ const ipfs = await this.createNode()
43
+ // console.log('ipfs: ', ipfs)
44
+
45
+ this.id = ipfs.libp2p.peerId.toString()
46
+ console.log('IPFS ID: ', this.id)
47
+
48
+ // Attempt to guess our ip4 IP address.
49
+ const ip4 = await this.publicIp()
50
+ let detectedMultiaddr = `/ip4/${ip4}/tcp/4001/p2p/${this.id}`
51
+ detectedMultiaddr = multiaddr(detectedMultiaddr)
52
+
53
+ // Get the multiaddrs for the node.
54
+ const multiaddrs = ipfs.libp2p.getMultiaddrs()
55
+ multiaddrs.push(detectedMultiaddr)
56
+ console.log('Multiaddrs: ', multiaddrs)
57
+
58
+ this.multiaddrs = multiaddrs
59
+
60
+ // Signal that this adapter is ready.
61
+ this.isReady = true
62
+
63
+ this.ipfs = ipfs
64
+
65
+ return this.ipfs
66
+ } catch (err) {
67
+ console.error('Error in start()')
68
+
69
+ throw err
70
+ }
71
+ }
72
+
73
+ // This function creates an IPFS node using Helia.
74
+ // It returns the node as an object.
75
+ async createNode () {
76
+ try {
77
+ // Create block and data stores.
78
+ const blockstore = new FsBlockstore(`${IPFS_DIR}/blockstore`)
79
+ const datastore = new FsDatastore(`${IPFS_DIR}/datastore`)
80
+
81
+ // Configure services
82
+ const services = {
83
+ identify: identifyService(),
84
+ pubsub: gossipsub({ allowPublishToZeroPeers: true })
85
+ }
86
+
87
+ // libp2p is the networking layer that underpins Helia
88
+ const libp2p = await createLibp2p({
89
+ datastore,
90
+ addresses: {
91
+ listen: [
92
+ '/ip4/127.0.0.1/tcp/0',
93
+ '/ip4/0.0.0.0/tcp/4001',
94
+ '/ip4/0.0.0.0/tcp/4003/ws',
95
+ '/webrtc'
96
+ ]
97
+ },
98
+ transports: [
99
+ tcp(),
100
+ webSockets(),
101
+ circuitRelayTransport({ discoverRelays: 3 }),
102
+ webRTC()
103
+ ],
104
+ connectionEncryption: [
105
+ noise()
106
+ ],
107
+ streamMuxers: [
108
+ yamux()
109
+ ],
110
+ services
111
+ })
112
+
113
+ // create a Helia node
114
+ const helia = await createHelia({
115
+ blockstore,
116
+ datastore,
117
+ libp2p
118
+ })
119
+
120
+ return helia
121
+ } catch (err) {
122
+ console.error('Error creating Helia node: ', err)
123
+
124
+ throw err
125
+ }
126
+ }
127
+
128
+ async stop () {
129
+ await this.ipfs.stop()
130
+
131
+ return true
132
+ }
133
+
134
+ // Ensure that the directories exist to store blocks from the IPFS network.
135
+ // This function is called at startup, before the IPFS node is started.
136
+ ensureBlocksDir () {
137
+ try {
138
+ !fs.existsSync(`${ROOT_DIR}.ipfsdata`) && fs.mkdirSync(`${ROOT_DIR}.ipfsdata`)
139
+
140
+ !fs.existsSync(`${IPFS_DIR}`) && fs.mkdirSync(`${IPFS_DIR}`)
141
+
142
+ !fs.existsSync(`${IPFS_DIR}/blockstore`) && fs.mkdirSync(`${IPFS_DIR}/blockstore`)
143
+
144
+ !fs.existsSync(`${IPFS_DIR}/datastore`) && fs.mkdirSync(`${IPFS_DIR}/datastore`)
145
+
146
+ return true
147
+ } catch (err) {
148
+ console.error('Error in ensureBlocksDir(): ', err)
149
+ throw err
150
+ }
151
+ }
152
+ }
153
+
154
+ export default CreateHeliaNode
@@ -0,0 +1,34 @@
1
+ /*
2
+ This is an example of how to start a Helia IPFS node with node.js and attach
3
+ the helia-coord library to it.
4
+ */
5
+
6
+ // Global npm libraries
7
+ import SlpWallet from 'minimal-slp-wallet'
8
+
9
+ // Local libraries
10
+ import IpfsCoord from '../index.js'
11
+ import CreateHeliaNode from './create-helia-node.js'
12
+
13
+ async function start () {
14
+ // Create an instance of bch-js and IPFS.
15
+ const wallet = new SlpWallet()
16
+ await wallet.walletInfoPromise
17
+
18
+ const createHeliaNode = new CreateHeliaNode()
19
+ const ipfs = await createHeliaNode.start()
20
+
21
+ // Pass bch-js and IPFS to ipfs-coord when instantiating it.
22
+ const ipfsCoord = new IpfsCoord({
23
+ ipfs,
24
+ wallet,
25
+ type: 'node.js',
26
+ // type: 'browser'
27
+ nodeType: 'external',
28
+ debugLevel: 2
29
+ })
30
+
31
+ await ipfsCoord.start()
32
+ console.log('IPFS and the coordination library is ready.')
33
+ }
34
+ start()
package/index.js CHANGED
@@ -34,6 +34,7 @@ class IpfsCoord {
34
34
  // 0 = no debug information.
35
35
  // 1 = status logs
36
36
  // 2 = verbose errors about peer connections
37
+ // 3 = everything
37
38
  this.debugLevel = parseInt(localConfig.debugLevel)
38
39
  if (!this.debugLevel) this.debugLevel = 0
39
40
  localConfig.debugLevel = this.debugLevel
@@ -80,15 +81,20 @@ class IpfsCoord {
80
81
  await this.adapters.ipfs.start()
81
82
 
82
83
  // Create an instance of the 'self' which represents this IPFS node, BCH
83
- // wallet, and other things that make up this ipfs-coord powered IPFS node.
84
+ // wallet, and other things that make up this helia-coord powered IPFS node.
84
85
  this.thisNode = await this.useCases.thisNode.createSelf({ type: this.type })
85
86
  // console.log('thisNode: ', this.thisNode)
86
87
 
88
+ // Pass instance of thisNode to the other use-case libraries.
89
+ this.useCases.peer.updateThisNode({ thisNode: this.thisNode, peerUseCases: this.useCases.peer })
90
+ this.useCases.pubsub.updateThisNode(this.thisNode)
91
+
87
92
  // Subscribe to Pubsub Channels
88
- await this.useCases.pubsub.initializePubsub(this.thisNode)
93
+ // await this.useCases.pubsub.initializePubsub(this.thisNode)
94
+ await this.useCases.pubsub.initializePubsub({ controllers: this.controllers })
89
95
 
90
96
  // Start timer-based controllers.
91
- await this.controllers.timer.startTimers(this.thisNode, this.useCases)
97
+ await this.controllers.timer.startTimers(this.thisNode)
92
98
 
93
99
  // Kick-off initial connection to Circuit Relays and Peers.
94
100
  // Note: Deliberatly *not* using await here, so that it doesn't block startup
@@ -104,15 +110,11 @@ class IpfsCoord {
104
110
  // after this initial function.
105
111
  async _initializeConnections () {
106
112
  try {
107
- // Connect to Circuit Relays
108
- await this.useCases.relays.initializeRelays(this.thisNode)
109
- console.log('Initial connections to Circuit Relays complete.')
110
-
111
113
  // Load list of Circuit Relays from GitHub Gist.
112
114
  await this.useCases.relays.getCRGist(this.thisNode)
113
115
  console.log('Finished connecting to Circuit Relays in GitHub Gist.')
114
116
 
115
- await this.useCases.thisNode.refreshPeerConnections()
117
+ await this.useCases.peer.refreshPeerConnections()
116
118
  console.log('Initial connections to subnet Peers complete.')
117
119
 
118
120
  return true
@@ -157,6 +157,9 @@ class IpfsAdapter {
157
157
  connectedPeers = connectedPeers.map(x => x.toString())
158
158
  this.log.statusLog(1, 'connectedPeers: ', connectedPeers)
159
159
 
160
+ // const connections = this.ipfs.libp2p.getConnections()
161
+ // console.log('connections: ', connections)
162
+
160
163
  return connectedPeers
161
164
  } catch (err) {
162
165
  console.error('Error in ipfs-adapter.js/getPeers(): ', err)
@@ -4,7 +4,6 @@
4
4
 
5
5
  // Local libraries
6
6
  import Messaging from './messaging.js'
7
- import AboutAdapter from './about-adapter.js'
8
7
  import { BroadcastRouter, PrivateChannelRouter } from './msg-router.js'
9
8
 
10
9
  // Libraries for working with default uint8Array Buffers that pubsub uses
@@ -42,7 +41,6 @@ class PubsubAdapter {
42
41
 
43
42
  // Encapsulate dependencies
44
43
  this.messaging = new Messaging(localConfig)
45
- this.about = new AboutAdapter(localConfig)
46
44
 
47
45
  this.nodeType = localConfig.nodeType
48
46
  if (!this.nodeType) {
@@ -54,10 +52,18 @@ class PubsubAdapter {
54
52
  this.handleNewMessage = this.handleNewMessage.bind(this)
55
53
  this.checkForDuplicateMsg = this.checkForDuplicateMsg.bind(this)
56
54
  this.manageMsgCache = this.manageMsgCache.bind(this)
55
+ this.injectMetricsHandler = this.injectMetricsHandler.bind(this)
57
56
 
58
57
  // State
59
58
  this.trackedMsgs = [] // Used reject repeated messages
60
59
  this.TRACKED_MSG_SIZE = 100
60
+ this.relayMetricsHandler = () => {} // placeholder
61
+ }
62
+
63
+ // This function is called by the peer use case library. RPC data for an
64
+ // /about call (used to measure peer metrics) is passed to this handler.
65
+ injectMetricsHandler (relayMetricsHandler) {
66
+ this.relayMetricsHandler = relayMetricsHandler
61
67
  }
62
68
 
63
69
  // Subscribe to a pubsub channel. Any data received on that channel is passed
@@ -98,7 +104,7 @@ class PubsubAdapter {
98
104
  }
99
105
  const privateRouter = new PrivateChannelRouter(pRouterOptions)
100
106
 
101
- // Subscribe to the pubsbu channel. Route any incoming messages to the
107
+ // Subscribe to the pubsub channel. Route any incoming messages to the
102
108
  // this library.
103
109
  this.ipfs.ipfs.libp2p.services.pubsub.subscribe(chanName.toString())
104
110
 
@@ -118,6 +124,26 @@ class PubsubAdapter {
118
124
  }
119
125
  }
120
126
 
127
+ // Subscribe to the general coordination pubsub channel
128
+ // Dev Note: I probably don't need to pass in the chanName, since I can pull
129
+ // that from the global config library.
130
+ async subscribeToCoordChannel (inObj = {}) {
131
+ try {
132
+ const { chanName, handler } = inObj
133
+
134
+ // Subscribe to the pubsub channel.
135
+ this.ipfs.ipfs.libp2p.services.pubsub.subscribe(chanName)
136
+
137
+ // Route incoming message events to the appropriate handler.
138
+ this.ipfs.ipfs.libp2p.services.pubsub.addEventListener('message', handler)
139
+
140
+ return true
141
+ } catch (err) {
142
+ console.error('Error in subscribeToCoordChannel()')
143
+ throw err
144
+ }
145
+ }
146
+
121
147
  // After the messaging.js library does the lower-level message handling and
122
148
  // decryption, it passes the message on to this function, which does any
123
149
  // additional parsing needed, and
@@ -182,7 +208,8 @@ class PubsubAdapter {
182
208
 
183
209
  // This event is handled by the about-adapter.js. It measures the
184
210
  // latency between peers.
185
- this.about.relayMetricsReceived(decryptedStr)
211
+ // this.about.relayMetricsReceived(decryptedStr)
212
+ this.relayMetricsHandler(decryptedStr)
186
213
 
187
214
  return data.result.value
188
215
  }
@@ -196,6 +223,7 @@ class PubsubAdapter {
196
223
  }
197
224
  }
198
225
 
226
+ // CT 1/21/24: This function will be deprecated and moved to pubsub use cases.
199
227
  // Attempts to parse data coming in from a pubsub channel. It is assumed that
200
228
  // the data is a string in JSON format. If it isn't, parsing will throw an
201
229
  // error and the message will be ignored.
@@ -259,6 +287,7 @@ class PubsubAdapter {
259
287
  }
260
288
  }
261
289
 
290
+ // CT 1/21/24: This function will be deprecated and moved to pubsub use cases.
262
291
  // Checks to see if a message has already been processed. This protects against
263
292
  // redundent processing.
264
293
  checkForDuplicateMsg (msg) {
@@ -282,6 +311,7 @@ class PubsubAdapter {
282
311
  return true
283
312
  }
284
313
 
314
+ // CT 1/21/24: This function will be deprecated and moved to pubsub use cases.
285
315
  // Keep the message queue to a resonable size.
286
316
  manageMsgCache () {
287
317
  // console.log(`trackedMsgs: `, this.trackedMsgs)
@@ -5,6 +5,7 @@
5
5
 
6
6
  // const TimerControllers = require('./timer-controller')
7
7
  import TimerControllers from './timer-controller.js'
8
+ import PubsubController from './pubsub-controller.js'
8
9
 
9
10
  class Controllers {
10
11
  constructor (localConfig = {}) {
@@ -15,9 +16,16 @@ class Controllers {
15
16
  'Instance of adapters required when instantiating Controllers'
16
17
  )
17
18
  }
19
+ this.useCases = localConfig.useCases
20
+ if (!this.useCases) {
21
+ throw new Error(
22
+ 'Instance of useCases required when instantiating Controllers'
23
+ )
24
+ }
18
25
 
19
26
  // Encapsulate dependencies
20
27
  this.timer = new TimerControllers(localConfig)
28
+ this.pubsub = new PubsubController(localConfig)
21
29
  }
22
30
  }
23
31