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.
- package/README.md +2 -0
- package/config/global-config.js +5 -1
- package/dev-docs/peers.md +0 -0
- package/dev-docs/pubsub.md +3 -0
- package/dev-docs/relays.md +0 -0
- package/dev-docs/startup.md +6 -0
- package/dev-docs/temp.md +62 -0
- package/dev-docs/theory-of-operation.md +21 -2
- package/dev-docs/this-node.md +0 -0
- package/dev-docs/usage-and-code.md +115 -0
- package/examples/create-helia-node.js +154 -0
- package/examples/start-node.js +34 -0
- package/index.js +10 -8
- package/lib/adapters/ipfs-adapter.js +3 -0
- package/lib/adapters/pubsub-adapter/index.js +34 -4
- package/lib/controllers/index.js +8 -0
- package/lib/controllers/pubsub-controller.js +69 -0
- package/lib/controllers/timer-controller.js +31 -2
- package/lib/entities/peers.js +3 -0
- package/lib/entities/pubsub.js +23 -0
- package/lib/entities/relays.js +3 -0
- package/lib/entities/this-node-entity.js +2 -40
- package/lib/use-cases/index.js +4 -1
- package/lib/use-cases/peer-use-cases.js +500 -0
- package/lib/use-cases/pubsub-use-cases.js +186 -13
- package/lib/use-cases/relay-use-cases.js +46 -62
- package/lib/use-cases/this-node-use-cases.js +3 -347
- package/package.json +13 -4
- package/test/mocks/adapter-mock.js +5 -2
- package/test/mocks/peers-mock.js +9 -2
- package/test/mocks/use-case-mocks.js +7 -2
- package/test/unit/adapters/gist.unit.adapters.js +18 -18
- package/test/unit/adapters/pubsub/pubsub-adapter-unit.js +16 -28
- package/test/unit/controllers/controllers-index-unit.js +15 -0
- package/test/unit/controllers/pubsub-controller.unit.js +107 -0
- package/test/unit/controllers/timer-controller-unit.js +21 -5
- package/test/unit/index-unit.js +2 -4
- package/test/unit/use-cases/peer.unit.use-cases.js +562 -1
- package/test/unit/use-cases/pubsub.unit.use-cases.js +168 -9
- package/test/unit/use-cases/relay-use-cases-unit.js +119 -64
- package/test/unit/use-cases/this-node-use-cases-unit.js +8 -358
- package/test/unit/use-cases/use-cases-index-unit.js +4 -1
- package/config/bootstrap-circuit-relays.js +0 -48
- package/examples/start-external.js +0 -56
- package/lib/adapters/pubsub-adapter/about-adapter.js +0 -117
- package/test/unit/adapters/pubsub/about-adapter-unit.js +0 -129
package/README.md
CHANGED
package/config/global-config.js
CHANGED
|
@@ -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
|
|
File without changes
|
package/dev-docs/temp.md
ADDED
|
@@ -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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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)
|
package/lib/controllers/index.js
CHANGED
|
@@ -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
|
|