2021-07-06 17:17:26 -07:00
|
|
|
/*
|
|
|
|
|
Clean Architecture Adapter for IPFS.
|
|
|
|
|
This library deals with IPFS so that the apps business logic doesn't need
|
|
|
|
|
to have any specific knowledge of the js-ipfs library.
|
2021-09-05 09:23:38 -07:00
|
|
|
|
|
|
|
|
TODO: Add the external IP address to the list of multiaddrs advertised by
|
|
|
|
|
this node. See this GitHub Issue for details:
|
|
|
|
|
https://github.com/Permissionless-Software-Foundation/ipfs-service-provider/issues/38
|
2021-07-06 17:17:26 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
// Global npm libraries
|
2023-10-22 07:51:45 -07:00
|
|
|
import { createHelia } from 'helia'
|
2022-09-08 08:58:39 -07:00
|
|
|
import fs from 'fs'
|
2023-10-22 07:51:45 -07:00
|
|
|
import { FsBlockstore } from 'blockstore-fs'
|
|
|
|
|
import { FsDatastore } from 'datastore-fs'
|
|
|
|
|
import { createLibp2p } from 'libp2p'
|
|
|
|
|
import { tcp } from '@libp2p/tcp'
|
|
|
|
|
import { noise } from '@chainsafe/libp2p-noise'
|
|
|
|
|
import { yamux } from '@chainsafe/libp2p-yamux'
|
|
|
|
|
import { bootstrap } from '@libp2p/bootstrap'
|
|
|
|
|
import { identifyService } from 'libp2p/identify'
|
|
|
|
|
import { gossipsub } from '@chainsafe/libp2p-gossipsub'
|
2021-07-06 17:17:26 -07:00
|
|
|
|
|
|
|
|
// Local libraries
|
2022-09-08 08:58:39 -07:00
|
|
|
import config from '../../../config/index.js'
|
2021-07-06 17:17:26 -07:00
|
|
|
|
2023-10-22 17:35:10 -07:00
|
|
|
// Hack to get __dirname back.
|
|
|
|
|
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
|
|
|
|
|
import * as url from 'url'
|
|
|
|
|
const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
|
|
|
|
|
console.log('__dirname: ', __dirname)
|
|
|
|
|
|
|
|
|
|
const IPFS_DIR = `${__dirname}../../../.ipfsdata/ipfs`
|
2021-12-06 12:46:22 -08:00
|
|
|
|
2021-07-06 17:17:26 -07:00
|
|
|
class IpfsAdapter {
|
|
|
|
|
constructor (localConfig) {
|
|
|
|
|
// Encapsulate dependencies
|
2021-12-29 17:28:35 -08:00
|
|
|
this.config = config
|
2022-09-08 08:58:39 -07:00
|
|
|
this.fs = fs
|
2023-10-22 07:51:45 -07:00
|
|
|
// this.create = create
|
2021-12-29 17:28:35 -08:00
|
|
|
|
|
|
|
|
// Choose the IPFS constructor based on the config settings.
|
2022-09-08 08:58:39 -07:00
|
|
|
// this.IPFS = IPFSembedded // default
|
|
|
|
|
// if (this.config.isProduction) {
|
|
|
|
|
// this.IPFS = IPFSexternal
|
|
|
|
|
// }
|
2021-07-06 17:17:26 -07:00
|
|
|
|
|
|
|
|
// Properties of this class instance.
|
|
|
|
|
this.isReady = false
|
2023-10-22 17:35:10 -07:00
|
|
|
|
|
|
|
|
// Bind 'this' object to all subfunctions
|
|
|
|
|
this.start = this.start.bind(this)
|
|
|
|
|
this.createNode = this.createNode.bind(this)
|
|
|
|
|
this.stop = this.stop.bind(this)
|
2021-07-06 17:17:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start an IPFS node.
|
|
|
|
|
async start () {
|
|
|
|
|
try {
|
2023-10-22 07:51:45 -07:00
|
|
|
// Create an IPFS node
|
|
|
|
|
const ipfs = await this.createNode()
|
|
|
|
|
// console.log('ipfs: ', ipfs)
|
2021-12-29 17:28:35 -08:00
|
|
|
|
2023-10-22 07:51:45 -07:00
|
|
|
// Get the multiaddrs for the node.
|
|
|
|
|
const multiaddrs = ipfs.libp2p.getMultiaddrs()
|
|
|
|
|
console.log('Multiaddrs: ', multiaddrs)
|
2021-07-06 17:17:26 -07:00
|
|
|
|
2023-10-22 07:51:45 -07:00
|
|
|
this.multiaddrs = multiaddrs
|
|
|
|
|
this.id = ipfs.libp2p.peerId.toString()
|
|
|
|
|
console.log('IPFS ID: ', this.id)
|
2021-09-28 15:44:06 -07:00
|
|
|
|
2021-07-06 17:17:26 -07:00
|
|
|
// Signal that this adapter is ready.
|
|
|
|
|
this.isReady = true
|
|
|
|
|
|
2023-10-22 07:51:45 -07:00
|
|
|
this.ipfs = ipfs
|
|
|
|
|
|
2021-07-06 17:17:26 -07:00
|
|
|
return this.ipfs
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Error in ipfs.js/start()')
|
2021-12-06 12:46:22 -08:00
|
|
|
|
|
|
|
|
// If IPFS crashes because the /blocks directory is full, wipe the directory.
|
2022-03-15 18:58:55 -04:00
|
|
|
// if (err.message.includes('No space left on device')) {
|
|
|
|
|
// this.rmBlocksDir()
|
|
|
|
|
// }
|
2021-12-06 12:46:22 -08:00
|
|
|
|
2021-07-06 17:17:26 -07:00
|
|
|
throw err
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-08 08:09:48 -07:00
|
|
|
|
2023-10-22 07:51:45 -07:00
|
|
|
// This function creates an IPFS node using Helia.
|
|
|
|
|
// It returns the node as an object.
|
|
|
|
|
async createNode () {
|
|
|
|
|
try {
|
|
|
|
|
// Create block and data stores.
|
|
|
|
|
const blockstore = new FsBlockstore(`${IPFS_DIR}/blockstore`)
|
|
|
|
|
const datastore = new FsDatastore(`${IPFS_DIR}/datastore`)
|
|
|
|
|
|
|
|
|
|
// libp2p is the networking layer that underpins Helia
|
|
|
|
|
const libp2p = await createLibp2p({
|
|
|
|
|
datastore,
|
|
|
|
|
addresses: {
|
|
|
|
|
listen: [
|
|
|
|
|
'/ip4/127.0.0.1/tcp/0',
|
|
|
|
|
`/ip4/0.0.0.0/tcp/${this.config.ipfsTcpPort}`
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
transports: [
|
|
|
|
|
tcp()
|
|
|
|
|
],
|
|
|
|
|
connectionEncryption: [
|
|
|
|
|
noise()
|
|
|
|
|
],
|
|
|
|
|
streamMuxers: [
|
|
|
|
|
yamux()
|
|
|
|
|
],
|
|
|
|
|
peerDiscovery: [
|
|
|
|
|
bootstrap({
|
|
|
|
|
list: [
|
|
|
|
|
// '/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN',
|
|
|
|
|
// '/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa',
|
|
|
|
|
// '/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb',
|
|
|
|
|
// '/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt'
|
|
|
|
|
'/ip4/5.161.108.190/tcp/4102/p2p/12D3KooWKNuBjaMgEDN2tGqzmdfM2bmd22VEuboC4X7x8ua4DvUg',
|
|
|
|
|
'/ip4/5.161.108.190/tcp/4001/p2p/12D3KooWMW3u8ygGHqNHjxXsw7TA39vgrSQEx7vEGoUh7EiCwGGv',
|
|
|
|
|
'/ip4/137.184.13.92/tcp/4001/p2p/12D3KooWMbpo92kSGwWiN6QH7fvstMWiLZKcxmReq3Hhbpya2bq4',
|
|
|
|
|
'/ip4/78.46.129.7/tcp/4001/p2p/12D3KooWJyc54njjeZGbLew4D8u1ghrmZTTPyh3QpBF7dxtd3zGY',
|
|
|
|
|
'/ip4/5.161.72.148/tcp/4001/p2p/12D3KooWAVG5kn46P9mjKCLUSPesmmLeKf2a79qRt6u22hJaEARJ',
|
|
|
|
|
'/ip4/161.35.99.207/tcp/4001/p2p/12D3KooWDtj9cfj1SKuLbDNKvKRKSsGN8qivq9M8CYpLPDpcD5pu',
|
|
|
|
|
'/ip4/192.168.2.173/tcp/4001/p2p/12D3KooWAz2hbriyU9o1wHNU2nxqG8mMgAdWnXCSfWiv5kxSfW49'
|
|
|
|
|
]
|
|
|
|
|
})
|
|
|
|
|
],
|
|
|
|
|
services: {
|
|
|
|
|
identify: identifyService(),
|
|
|
|
|
pubsub: gossipsub({ allowPublishToZeroPeers: true })
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// create a Helia node
|
|
|
|
|
const helia = await createHelia({
|
|
|
|
|
blockstore,
|
|
|
|
|
datastore,
|
|
|
|
|
libp2p
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return helia
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Error creating Helia node: ', err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-08 08:09:48 -07:00
|
|
|
async stop () {
|
|
|
|
|
await this.ipfs.stop()
|
2021-09-30 15:20:51 -07:00
|
|
|
|
|
|
|
|
return true
|
2021-08-08 08:09:48 -07:00
|
|
|
}
|
2021-12-06 12:46:22 -08:00
|
|
|
|
|
|
|
|
// Remove the '/blocks' directory that is used to store IPFS data.
|
|
|
|
|
// Dev Note: It's assumed this node is not pinning any data and that
|
|
|
|
|
// everything in this directory is transient. This folder will regularly
|
|
|
|
|
// fill up and prevent IPFS from starting.
|
2022-03-15 18:58:55 -04:00
|
|
|
// rmBlocksDir () {
|
|
|
|
|
// try {
|
|
|
|
|
// const dir = `${IPFS_DIR}/blocks`
|
|
|
|
|
// console.log(`Deleting ${dir} directory...`)
|
|
|
|
|
//
|
|
|
|
|
// this.fs.rmdirSync(dir, { recursive: true })
|
|
|
|
|
//
|
|
|
|
|
// console.log(`${dir} directory is deleted!`)
|
|
|
|
|
//
|
|
|
|
|
// return true // Signal successful execution.
|
|
|
|
|
// } catch (err) {
|
|
|
|
|
// console.log('Error in rmBlocksDir()')
|
|
|
|
|
// throw err
|
|
|
|
|
// }
|
|
|
|
|
// }
|
2021-07-06 17:17:26 -07:00
|
|
|
}
|
|
|
|
|
|
2022-09-08 08:58:39 -07:00
|
|
|
export default IpfsAdapter
|