Compare commits

...
31 Commits
Author SHA1 Message Date
Chris Troutner 673a9c8c7a Merge pull request #80 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider
2026-03-11 11:10:39 -07:00
Chris Troutner 4d4b678447 fixing merge conflicts 2026-03-11 11:07:54 -07:00
Chris Troutner e9606ecfe8 Merge pull request #188 from Permissionless-Software-Foundation/ct-unstable
fix(helia-coord): Updating to v2.0.3 circuit relay file tx
2026-03-06 20:07:55 -08:00
Chris Troutner bfc0a5be9f fix(helia-coord): Updating to v2.0.3 circuit relay file tx 2026-03-06 20:05:04 -08:00
Chris Troutner af68e411bd Merge pull request #187 from Permissionless-Software-Foundation/ct-unstable
Updating READMe
2026-02-17 09:34:47 -07:00
Chris Troutner b9b79c2142 Updating READMe 2026-02-17 09:33:11 -07:00
Chris Troutner 58f24954ca Merge pull request #79 from Permissionless-Software-Foundation/ct-unstable
Syncing with upstream ipfs-service-provider
2026-02-16 08:52:14 -07:00
Chris Troutner 6f4f21b373 Fixing npm ci 2026-02-16 08:49:17 -07:00
Chris Troutner b0e42bb956 fixing merge conflicts 2026-02-16 08:43:01 -07:00
Chris Troutner efb02a4381 fix(helia-coord): Updating to v2.0.2 2026-02-16 08:41:49 -07:00
Chris Troutner d894c1e596 Merge pull request #186 from Permissionless-Software-Foundation/ct-unstable
Helia v2.x
2026-02-11 13:27:29 -07:00
Chris Troutner 821cf4abe8 feat(helia-coord): Refactoring to use new v2.x helia-coord with breaking changes 2026-02-11 13:23:57 -07:00
Chris Troutner 74696cfb9e Updated to latest helia-coord v2 version 2026-02-11 11:41:26 -07:00
Chris Troutner 0bfb84e21b Merge pull request #185 from Permissionless-Software-Foundation/ct-unstable
fix(helia-coord): Updating to v1.10.0 with DHT
2026-02-10 17:22:54 -07:00
Chris Troutner 35b4e7079d fix(helia-coord): Updating to v1.10.0 with DHT 2026-02-10 17:19:13 -07:00
Chris Troutner 7d369859a5 Fixing merge conflicts 2026-02-03 15:32:50 -07:00
Chris Troutner 75ea6ecdde Merge pull request #184 from Permissionless-Software-Foundation/ct-unstable
fix(Docker): Updating to Ubuntu 24 & node.js v22
2026-02-03 14:07:59 -07:00
Chris Troutner d4c9665c16 fix(Docker): Updating to Ubuntu 24 & node.js v22 2026-02-03 14:05:15 -07:00
Chris Troutner 768bfdb4e7 Merge pull request #183 from Permissionless-Software-Foundation/ct-unstable
Updating dependencies
2026-02-02 18:56:13 -07:00
Chris Troutner 764fe928d2 fix(minimal-slp-wallet): Updating to v7.1.4 2026-02-02 18:54:18 -07:00
Chris Troutner 2de61458d3 fix(deps): Updating dependencies 2026-02-02 18:41:50 -07:00
Chris Troutner d38e371a05 Merge pull request #182 from Permissionless-Software-Foundation/ct-unstable
fix(package.json): Fixing dependency issues with overrides
2026-02-02 18:27:27 -07:00
Chris Troutner a68ca92468 fix(package.json): Fixing dependency issues with overrides 2026-02-02 18:25:18 -07:00
Chris Troutner 7c26e33a8c Merge pull request #181 from Permissionless-Software-Foundation/ct-unstable
fix(util): Fixing problem with updateUser.js script
2025-10-25 14:33:12 -07:00
Chris Troutner 2595ee8295 fix(util): Fixing problem with updateUser.js script 2025-10-25 14:31:33 -07:00
Chris Troutner 476fdcd0aa Merge pull request #180 from Permissionless-Software-Foundation/ct-unstable
fix(utils): Adding script to update users password or convert to admin
2025-10-25 14:24:43 -07:00
Chris Troutner b6762b215e fix(utils): Adding script to update users password or convert to admin 2025-10-25 14:23:28 -07:00
Chris Troutner 8faadfc5c1 Merge pull request #78 from Permissionless-Software-Foundation/ct-unstable
fix(ipfs-coord): Reducing screen spam when waiting to connect to pref…
2025-10-16 12:47:41 -07:00
Chris Troutner fe0ad56395 fix(ipfs-coord): Reducing screen spam when waiting to connect to preferred service provider 2025-10-16 12:45:01 -07:00
Chris Troutner 0e6a411012 Merge pull request #179 from Permissionless-Software-Foundation/ct-unstable
fix(IPFS): Adding API docs for the IPFS endpoints that need it
2025-10-04 08:32:55 -07:00
Chris Troutner 55e9e35c1f fix(IPFS): Adding API docs for the IPFS endpoints that need it 2025-10-04 08:31:17 -07:00
15 changed files with 5896 additions and 7047 deletions
+2 -2
View File
@@ -1,8 +1,8 @@
{ {
"email": "system@system.com", "email": "system@system.com",
"name": "admin", "name": "admin",
"password": "SyP9YCFkgxu2FPNxFI8W", "password": "c9JjJxZmTs5bWmWC6adQ",
"type": "admin", "type": "admin",
"id": "68d47521e6060ba7332301b1", "id": "68d47521e6060ba7332301b1",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY4ZDQ3NTIxZTYwNjBiYTczMzIzMDFiMSIsImlhdCI6MTc1OTE3NjQ3Mn0.pGJh_Io1_aZ3IwWdzOnC5Bt9Nw0x4MRhAAp7RkZrtmA" "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY4ZDQ3NTIxZTYwNjBiYTczMzIzMDFiMSIsImlhdCI6MTc2MDY0MzI5NH0.SgoWnrP0YZZtPZ2V_C7HsfKoRHphfoIODN3-pltsGbo"
} }
+3 -3
View File
@@ -1,8 +1,8 @@
{ {
"email": "system@system.com", "email": "system@system.com",
"name": "admin", "name": "admin",
"password": "UdbCjMgm7T8efeyUfcdN", "password": "NR0S1fPGLOAGUwPfKpJe",
"type": "admin", "type": "admin",
"id": "68daea62e9b47af49f86dae9", "id": "698277538440afae85603e83",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY4ZGFlYTYyZTliNDdhZjQ5Zjg2ZGFlOSIsImlhdCI6MTc1OTE3NzMxNH0.-mFCzyDri0OLMz-oM03qyYPj7srEH_6HFLGzgCf_JLI" "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY5ODI3NzUzODQ0MGFmYWU4NTYwM2U4MyIsImlhdCI6MTc3MDE1NzkwN30.rBILkWIXFtj0OkGBQyJyf89i9S2SYhm4ETPhvevVwaQ"
} }
+5723 -6646
View File
File diff suppressed because it is too large Load Diff
+11 -21
View File
@@ -25,28 +25,10 @@
}, },
"repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-consumer", "repository": "Permissionless-Software-Foundation/ipfs-bch-wallet-consumer",
"dependencies": { "dependencies": {
"@chainsafe/libp2p-gossipsub": "14.1.0",
"@chainsafe/libp2p-noise": "16.0.1",
"@chainsafe/libp2p-yamux": "7.0.1",
"@helia/unixfs": "4.0.2",
"@libp2p/bootstrap": "11.0.26",
"@libp2p/circuit-relay-v2": "3.2.2",
"@libp2p/config": "1.1.0",
"@libp2p/identify": "3.0.22",
"@libp2p/keychain": "5.0.14",
"@libp2p/logger": "5.1.8",
"@libp2p/ping": "2.0.22",
"@libp2p/tcp": "10.1.2",
"@libp2p/webrtc": "5.2.2",
"@libp2p/websockets": "9.2.2",
"@multiformats/multiaddr": "12.3.5",
"axios": "0.27.2", "axios": "0.27.2",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blockstore-fs": "2.0.2",
"datastore-fs": "10.0.2",
"glob": "7.1.6", "glob": "7.1.6",
"helia": "5.2.1", "helia-coord": "2.0.3",
"helia-coord": "1.7.2",
"jsonrpc-lite": "2.2.0", "jsonrpc-lite": "2.2.0",
"jsonwebtoken": "8.5.1", "jsonwebtoken": "8.5.1",
"jwt-bch-lib": "1.3.0", "jwt-bch-lib": "1.3.0",
@@ -61,10 +43,9 @@
"koa-router": "10.0.0", "koa-router": "10.0.0",
"koa-static": "5.0.0", "koa-static": "5.0.0",
"koa2-ratelimit": "0.9.1", "koa2-ratelimit": "0.9.1",
"libp2p": "2.7.2",
"line-reader": "0.4.0", "line-reader": "0.4.0",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"minimal-slp-wallet": "5.13.2", "minimal-slp-wallet": "7.1.4",
"mongoose": "5.13.14", "mongoose": "5.13.14",
"node-fetch": "npm:@achingbrain/node-fetch@2.6.7", "node-fetch": "npm:@achingbrain/node-fetch@2.6.7",
"nodemailer": "6.7.5", "nodemailer": "6.7.5",
@@ -105,5 +86,14 @@
"ignore": [ "ignore": [
"/test/unit/mocks/**/*.js" "/test/unit/mocks/**/*.js"
] ]
},
"overrides": {
"@multiformats/multiaddr-to-uri": "10.1.2",
"@multiformats/multiaddr-matcher": "1.6.0",
"@libp2p/utils": "6.7.1",
"helia": {
"libp2p": "2.7.2",
"@libp2p/upnp-nat": "3.0.0"
}
} }
} }
+2 -2
View File
@@ -4,7 +4,7 @@
#IMAGE BUILD COMMANDS #IMAGE BUILD COMMANDS
# ct-base-ubuntu = ubuntu 18.04 + nodejs v10 LTS # ct-base-ubuntu = ubuntu 18.04 + nodejs v10 LTS
#FROM christroutner/ct-base-ubuntu #FROM christroutner/ct-base-ubuntu
FROM ubuntu:20.04 FROM ubuntu:24.04
MAINTAINER Chris Troutner <chris.troutner@gmail.com> MAINTAINER Chris Troutner <chris.troutner@gmail.com>
#Update the OS and install any OS packages needed. #Update the OS and install any OS packages needed.
@@ -15,7 +15,7 @@ RUN apt-get install -y sudo git curl nano gnupg wget
RUN sudo apt-get install -y ca-certificates curl gnupg RUN sudo apt-get install -y ca-certificates curl gnupg
RUN sudo mkdir -p /etc/apt/keyrings RUN sudo mkdir -p /etc/apt/keyrings
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
RUN sudo apt-get update RUN sudo apt-get update
RUN sudo apt-get install -y nodejs build-essential RUN sudo apt-get install -y nodejs build-essential
+1 -1
View File
@@ -41,7 +41,7 @@ class Adapters {
this.passport = new Passport() this.passport = new Passport()
this.nodemailer = new Nodemailer() this.nodemailer = new Nodemailer()
this.jsonFiles = new JSONFiles() this.jsonFiles = new JSONFiles()
this.bchjs = new BCHJS() this.bchjs = new BCHJS({ restURL: config.apiServer })
this.config = config this.config = config
this.bch = new BCH(localConfig) this.bch = new BCH(localConfig)
this.p2wdb = new P2WDB(localConfig) this.p2wdb = new P2WDB(localConfig)
+6 -3
View File
@@ -277,6 +277,7 @@ class IpfsCoordAdapter {
// Persist the config setting, so it can be used by other commands. // Persist the config setting, so it can be used by other commands.
// _this.conf.set('selectedService', thisPeer) // _this.conf.set('selectedService', thisPeer)
console.log(`---->BCH wallet service selected: ${thisPeer}`) console.log(`---->BCH wallet service selected: ${thisPeer}`)
console.log('preferredProvider: ', _this.config.preferredProvider)
} }
// console.log('preferredProvider: ', _this.config.preferredProvider) // console.log('preferredProvider: ', _this.config.preferredProvider)
@@ -287,11 +288,12 @@ class IpfsCoordAdapter {
_this.config.preferredProvider && _this.config.preferredProvider &&
thisPeer === _this.config.preferredProvider thisPeer === _this.config.preferredProvider
) { ) {
console.log('preferredProvider: ', _this.config.preferredProvider) // console.log('preferredProvider: ', _this.config.preferredProvider)
console.log('thisPeer: ', thisPeer) // console.log('thisPeer: ', thisPeer)
console.log('selectedServiceProvider: ', _this.state.selectedServiceProvider) // console.log('selectedServiceProvider: ', _this.state.selectedServiceProvider)
if (_this.state.selectedServiceProvider !== thisPeer) { if (_this.state.selectedServiceProvider !== thisPeer) {
console.log(`---->BCH wallet service switched to preferred peer: ${thisPeer}`) console.log(`---->BCH wallet service switched to preferred peer: ${thisPeer}`)
console.log('preferredProvider: ', _this.config.preferredProvider)
} }
_this.state.selectedServiceProvider = thisPeer _this.state.selectedServiceProvider = thisPeer
@@ -370,6 +372,7 @@ class IpfsCoordAdapter {
// Persist the config setting, so it can be used by other commands. // Persist the config setting, so it can be used by other commands.
// _this.conf.set('selectedService', thisPeer) // _this.conf.set('selectedService', thisPeer)
console.log(`---->IPFS File service selected: ${thisPeer}`) console.log(`---->IPFS File service selected: ${thisPeer}`)
console.log('preferredIpfsFileProvider: ', _this.config.preferredIpfsFileProvider)
} }
// console.log('preferredProvider: ', _this.config.preferredProvider) // console.log('preferredProvider: ', _this.config.preferredProvider)
+17 -228
View File
@@ -3,36 +3,12 @@
This library deals with IPFS so that the apps business logic doesn't need This library deals with IPFS so that the apps business logic doesn't need
to have any specific knowledge of the js-ipfs library. to have any specific knowledge of the js-ipfs library.
TODO: Add the external IP address to the list of multiaddrs advertised by Node creation is delegated to helia-coord's CreateHeliaNode factory,
this node. See this GitHub Issue for details: which encapsulates all helia/libp2p dependencies and configuration.
https://github.com/Permissionless-Software-Foundation/ipfs-service-provider/issues/38
*/ */
// Global npm libraries // Global npm libraries
import { createHelia } from 'helia' import CreateHeliaNode from 'helia-coord/create-helia-node'
import fs from 'fs'
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 { identify, identifyPush } from '@libp2p/identify'
import { circuitRelayServer, circuitRelayTransport } from '@libp2p/circuit-relay-v2'
// import { circuitRelayServer } from '@libp2p/circuit-relay-v2'
import { gossipsub } from '@chainsafe/libp2p-gossipsub'
import { webSockets } from '@libp2p/websockets'
import { publicIpv4 } from 'public-ip'
import { multiaddr } from '@multiformats/multiaddr'
import { webRTC, webRTCDirect } from '@libp2p/webrtc'
import { keychain } from '@libp2p/keychain'
import { unixfs } from '@helia/unixfs'
// import { generateKeyPairFromSeed } from '@libp2p/crypto/keys'
// import crypto from 'crypto'
import { ping } from '@libp2p/ping'
import { loadOrCreateSelfKey } from '@libp2p/config'
// Local libraries // Local libraries
import config from '../../../config/index.js' import config from '../../../config/index.js'
@@ -42,59 +18,44 @@ import JsonFiles from '../json-files.js'
// https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/ // https://blog.logrocket.com/alternatives-dirname-node-js-es-modules/
import * as url from 'url' import * as url from 'url'
const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
// console.log('__dirname: ', __dirname)
const ROOT_DIR = `${__dirname}../../../`
const IPFS_DIR = `${__dirname}../../../.ipfsdata/ipfs` const IPFS_DIR = `${__dirname}../../../.ipfsdata/ipfs`
class IpfsAdapter { class IpfsAdapter {
constructor (localConfig) { constructor (localConfig) {
// Encapsulate dependencies // Encapsulate dependencies
this.config = config this.config = config
this.fs = fs
this.createLibp2p = createLibp2p
this.publicIp = publicIpv4
this.multiaddr = multiaddr
this.jsonFiles = new JsonFiles() this.jsonFiles = new JsonFiles()
this.keychain = keychain
this.createHelia = createHelia
// Properties of this class instance. // Properties of this class instance.
this.isReady = false this.isReady = false
// Bind 'this' object to all subfunctions // Bind 'this' object to all subfunctions
this.start = this.start.bind(this) this.start = this.start.bind(this)
this.createNode = this.createNode.bind(this)
this.stop = this.stop.bind(this) this.stop = this.stop.bind(this)
this.ensureBlocksDir = this.ensureBlocksDir.bind(this)
// this.getPrivateKey = this.getPrivateKey.bind(this)
this.getSeed = this.getSeed.bind(this) this.getSeed = this.getSeed.bind(this)
} }
// Start an IPFS node. // Start an IPFS node.
async start () { async start () {
try { try {
// Ensure the directory structure exists that is needed by the IPFS node to store data. // Create the helia-coord node factory with production configuration.
this.ensureBlocksDir() this.heliaNode = new CreateHeliaNode({
ipfsDir: IPFS_DIR,
tcpPort: this.config.ipfsTcpPort,
wsPort: this.config.ipfsWsPort,
isCircuitRelay: this.config.isCircuitRelay,
getSeed: this.getSeed
})
// Create an IPFS node // Start the IPFS node (creates dirs, libp2p, helia, detects public IP)
const ipfs = await this.createNode() const ipfs = await this.heliaNode.start()
// console.log('ipfs: ', ipfs)
this.id = this.heliaNode.id
this.multiaddrs = this.heliaNode.multiaddrs
this.id = ipfs.libp2p.peerId.toString()
console.log('IPFS ID: ', this.id) console.log('IPFS ID: ', this.id)
console.log('Multiaddrs: ', this.multiaddrs)
// Attempt to guess our ip4 IP address.
const ip4 = await this.publicIp()
let detectedMultiaddr = `/ip4/${ip4}/tcp/${this.config.ipfsTcpPort}/p2p/${this.id}`
detectedMultiaddr = this.multiaddr(detectedMultiaddr)
// Get the multiaddrs for the node.
const multiaddrs = ipfs.libp2p.getMultiaddrs()
multiaddrs.push(detectedMultiaddr)
console.log('Multiaddrs: ', multiaddrs)
this.multiaddrs = multiaddrs
// Signal that this adapter is ready. // Signal that this adapter is ready.
this.isReady = true this.isReady = true
@@ -104,111 +65,6 @@ class IpfsAdapter {
return this.ipfs return this.ipfs
} catch (err) { } catch (err) {
console.error('Error in ipfs.js/start()') console.error('Error in ipfs.js/start()')
// If IPFS crashes because the /blocks directory is full, wipe the directory.
// if (err.message.includes('No space left on device')) {
// this.rmBlocksDir()
// }
throw err
}
}
// 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`)
// Get the private key.
// const privateKey = await this.getPrivateKey()
// console.log('privateKey: ', privateKey)
// Create an identity
const keychainInit = {
selfKey: 'myKey',
pass: await this.getSeed()
}
const privateKey = await loadOrCreateSelfKey(datastore, keychainInit)
// Configure services
const services = {
identify: identify(),
identifyPush: identifyPush(),
pubsub: gossipsub({ allowPublishToZeroTopicPeers: true }),
ping: ping(),
keychain: keychain(keychainInit)
}
if (this.config.isCircuitRelay) {
console.log('Helia (IPFS) node IS configured as Circuit Relay')
services.relay = circuitRelayServer({ // makes the node function as a relay server
hopTimeout: 30 * 1000, // incoming relay requests must be resolved within this time limit
reservations: {
maxReservations: 15, // how many peers are allowed to reserve relay slots on this server
reservationClearInterval: 300 * 1000, // how often to reclaim stale reservations
applyDefaultLimit: true, // whether to apply default data/duration limits to each relayed connection
defaultDurationLimit: 2 * 60 * 1000, // the default maximum amount of time a relayed connection can be open for
defaultDataLimit: BigInt(2 << 7) // the default maximum number of bytes that can be transferred over a relayed connection
},
maxInboundHopStreams: 32, // how many inbound HOP streams are allow simultaneously
maxOutboundHopStreams: 64 // how many outbound HOP streams are allow simultaneously
})
} else {
console.log('Helia (IPFS) node IS NOT configured as Circuit Relay')
}
const transports = [
tcp(),
webSockets(),
circuitRelayTransport({
discoverRelays: 3,
reservationConcurrency: 3
}),
webRTC(),
webRTCDirect()
]
// libp2p is the networking layer that underpins Helia
const libp2p = await this.createLibp2p({
// peerId,
privateKey,
datastore,
addresses: {
listen: [
'/ip4/127.0.0.1/tcp/0',
`/ip4/0.0.0.0/tcp/${this.config.ipfsTcpPort}`,
`/ip4/0.0.0.0/tcp/${this.config.ipfsWsPort}/ws`,
'/webrtc',
'/p2p-circuit'
]
},
transports,
connectionEncrypters: [
noise()
],
streamMuxers: [
yamux()
],
services
})
// create a Helia node
const helia = await this.createHelia({
blockstore,
datastore,
libp2p
})
// Attach IPFS file system.
const fs = unixfs(helia)
helia.fs = fs
return helia
} catch (err) {
console.error('Error creating Helia node: ', err)
throw err throw err
} }
} }
@@ -219,29 +75,6 @@ class IpfsAdapter {
return true return true
} }
// Ensure that the directories exist to store blocks from the IPFS network.
// This function is called at startup, before the IPFS node is started.
ensureBlocksDir () {
try {
!this.fs.existsSync(`${ROOT_DIR}.ipfsdata`) && this.fs.mkdirSync(`${ROOT_DIR}.ipfsdata`)
!this.fs.existsSync(`${IPFS_DIR}`) && this.fs.mkdirSync(`${IPFS_DIR}`)
!this.fs.existsSync(`${IPFS_DIR}/blockstore`) && this.fs.mkdirSync(`${IPFS_DIR}/blockstore`)
!this.fs.existsSync(`${IPFS_DIR}/datastore`) && this.fs.mkdirSync(`${IPFS_DIR}/datastore`)
!this.fs.existsSync(`${IPFS_DIR}/datastore/pkcs8`) && this.fs.mkdirSync(`${IPFS_DIR}/datastore/pkcs8`)
// !fs.existsSync(`${IPFS_DIR}/datastore/peers`) && fs.mkdirSync(`${IPFS_DIR}/datastore/peers`)
return true
} catch (err) {
console.error('Error in adapters/ipfs.js/ensureBlocksDir(): ', err)
throw err
}
}
// This function opens the seed used to generate the key for this IPFS peer. // This function opens the seed used to generate the key for this IPFS peer.
// The seed is stored in a JSON file. If it doesn't exist, a new one is created. // The seed is stored in a JSON file. If it doesn't exist, a new one is created.
async getSeed () { async getSeed () {
@@ -261,56 +94,12 @@ class IpfsAdapter {
await this.jsonFiles.writeJSON(seed, filename) await this.jsonFiles.writeJSON(seed, filename)
} }
// console.log('getSeed() seed: ', seed)
return seed return seed
} catch (err) { } catch (err) {
console.error('Error in adapters/ipfs/ipfs.js/getSeed()') console.error('Error in adapters/ipfs/ipfs.js/getSeed()')
throw err throw err
} }
} }
// Get the private key from disk, or generate a new one and save it,
// if it doesn't exist.
// async getPrivateKey () {
// try {
// const filename = `${IPFS_DIR}/privkey.json`
// let privKeyHex
// try {
// // Try to read the JSON file containing the seed.
// privKeyHex = await this.jsonFiles.readJSON(filename)
// // console.log('saved privKeyHex: ', privKeyHex)
// } catch (err) {
// // Generate a new private key and save it to disk.
// // Generate a new private key and save it to disk.
// const randomBuffer = crypto.randomBytes(32)
// // console.log('randomBuffer: ', randomBuffer)
// privKeyHex = randomBuffer.toString('hex')
// // console.log('new privKeyHex: ', privKeyHex)
// // Save the newly generated seed
// await this.jsonFiles.writeJSON(privKeyHex, filename)
// }
// // Convert the saved hex string to a buffer.
// const privKeyBuf = Buffer.from(privKeyHex, 'hex')
// // Convert the buffer to a Uint8Array 'seed'
// const seed = new Uint8Array(privKeyBuf.buffer, privKeyBuf.byteOffset, privKeyBuf.byteLength)
// // console.log('seed: ', seed)
// // Generate a ED25519 key pair.
// const privKey = await generateKeyPairFromSeed('Ed25519', seed)
// return privKey
// } catch (err) {
// console.error('Error in adapters/ipfs/ipfs.js/getPrivateKey(): ', err)
// throw err
// }
// }
} }
export default IpfsAdapter export default IpfsAdapter
+56 -1
View File
@@ -66,7 +66,26 @@ class IpfsRESTControllerLib {
} }
} }
// Return information on IPFS peers this node is connected to. /**
* @api {post} /ipfs/peers Get information on IPFS peers this node is connected to
* @apiPermission public
* @apiName GetIpfsPeers
* @apiGroup REST IPFS
*
* @apiParam {Boolean} [showAll=false] Whether to include detailed peer data
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST localhost:5001/ipfs/peers \
* -d '{"showAll": false}'
*
* @apiSuccess {Object[]} peers Array of peer objects
* @apiSuccess {String} peers[].peer Peer ID
* @apiSuccess {String} peers[].name Peer name
* @apiSuccess {String} peers[].protocol Protocol used by the peer
* @apiSuccess {String} peers[].version Peer version
* @apiSuccess {String} peers[].connectionAddr Connection address
* @apiSuccess {Object} [peers[].peerData] Detailed peer data (when showAll=true)
*/
async getPeers (ctx) { async getPeers (ctx) {
try { try {
const showAll = ctx.request.body.showAll const showAll = ctx.request.body.showAll
@@ -81,6 +100,24 @@ class IpfsRESTControllerLib {
} }
} }
/**
* @api {post} /ipfs/relays Get data about the known Circuit Relays
* @apiPermission public
* @apiName GetIpfsRelays
* @apiGroup REST IPFS
*
* @apiDescription Returns information about Circuit Relays, both v1 and v2, that this node knows about. V2 relays are hydrated with peer data from the connected peers list.
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST localhost:5001/ipfs/relays
*
* @apiSuccess {Object} relays Object containing relay information
* @apiSuccess {Object[]} relays.v2Relays Array of v2 Circuit Relay objects
* @apiSuccess {String} relays.v2Relays[].ipfsId IPFS ID of the relay
* @apiSuccess {String} relays.v2Relays[].name Name of the relay (hydrated from peer data)
* @apiSuccess {String} relays.v2Relays[].description Description of the relay (hydrated from peer data)
* @apiSuccess {Object[]} relays.v1Relays Array of v1 Circuit Relay configurations
*/
// Get data about the known Circuit Relays. Hydrate with data from peers list. // Get data about the known Circuit Relays. Hydrate with data from peers list.
async getRelays (ctx) { async getRelays (ctx) {
try { try {
@@ -94,6 +131,24 @@ class IpfsRESTControllerLib {
} }
} }
/**
* @api {post} /ipfs/connect Connect to a specific IPFS peer
* @apiPermission public
* @apiName ConnectToIpfsPeer
* @apiGroup REST IPFS
*
* @apiDescription Attempts to establish a connection to a specific IPFS peer using the provided multiaddr. Optionally returns detailed information about the connection.
*
* @apiParam {String} multiaddr Multiaddress of the peer to connect to (required)
* @apiParam {Boolean} [getDetails=false] Whether to return detailed connection information
*
* @apiExample Example usage:
* curl -H "Content-Type: application/json" -X POST localhost:5001/ipfs/connect \
* -d '{"multiaddr": "/ip4/161.35.99.207/tcp/4001/p2p/12D3KooWDtj9cfj1SKuLbDNKvKRKSsGN8qivq9M8CYpLPDpcD5pu", "getDetails": false}'
*
* @apiSuccess {Boolean} success Indicates whether the connection attempt was successful
* @apiSuccess {Object} [details] Additional connection details (when getDetails=true)
*/
async connect (ctx) { async connect (ctx) {
try { try {
const multiaddr = ctx.request.body.multiaddr const multiaddr = ctx.request.body.multiaddr
+4 -3
View File
@@ -14,13 +14,14 @@ describe('#adapters', () => {
let uut, sandbox let uut, sandbox
beforeEach(() => { beforeEach(() => {
uut = new Adapters()
sandbox = sinon.createSandbox() sandbox = sinon.createSandbox()
uut = new Adapters()
}) })
afterEach(() => { afterEach(() => {
sandbox.restore() if (sandbox) {
sandbox.restore()
}
}) })
describe('#start', () => { describe('#start', () => {
+28 -133
View File
@@ -5,26 +5,17 @@
// Global npm libraries // Global npm libraries
import { assert } from 'chai' import { assert } from 'chai'
import sinon from 'sinon' import sinon from 'sinon'
import cloneDeep from 'lodash.clonedeep'
// import { peerIdFromString } from '@libp2p/peer-id'
// Local libraries // Local libraries
import IPFSLib from '../../../src/adapters/ipfs/ipfs.js' import IPFSLib from '../../../src/adapters/ipfs/ipfs.js'
// import create from '../mocks/ipfs-mock.js'
import config from '../../../config/index.js'
import createHeliaLib from '../mocks/helia-mock.js'
// config.isProduction = true;
describe('#IPFS-adapter', () => { describe('#IPFS-adapter', () => {
let uut let uut
let sandbox let sandbox
let ipfs
beforeEach(() => { beforeEach(() => {
uut = new IPFSLib() uut = new IPFSLib()
ipfs = cloneDeep(createHeliaLib)
sandbox = sinon.createSandbox() sandbox = sinon.createSandbox()
}) })
@@ -39,26 +30,37 @@ describe('#IPFS-adapter', () => {
assert.isFunction(_uut.start) assert.isFunction(_uut.start)
assert.isFunction(_uut.stop) assert.isFunction(_uut.stop)
}) })
it('should instantiate dev IPFS Lib in production mode.', async () => {
config.isProduction = true
const _uut = new IPFSLib()
assert.exists(_uut)
assert.isFunction(_uut.start)
assert.isFunction(_uut.stop)
config.isProduction = false
})
}) })
describe('#start', () => { describe('#start', () => {
it('should return a promise that resolves into an instance of IPFS.', async () => { it('should return a promise that resolves into an instance of IPFS.', async () => {
// Mock dependencies. // Mock the heliaNode that gets created inside start()
sandbox.stub(uut, 'createNode').resolves(ipfs) const mockIpfs = {
sandbox.stub(uut, 'publicIp').resolves('192.168.2.4') libp2p: {
sandbox.stub(uut, 'multiaddr').returns('/ip4/fake-multiaddr') getMultiaddrs: () => [],
peerId: 'fake-id'
}
}
// Override the start method to mock the heliaNode creation
uut.start = async function () {
this.heliaNode = {
start: async () => mockIpfs,
id: 'fake-id',
multiaddrs: ['/ip4/fake-multiaddr']
}
const ipfs = await this.heliaNode.start()
this.id = this.heliaNode.id
this.multiaddrs = this.heliaNode.multiaddrs
this.isReady = true
this.ipfs = ipfs
return this.ipfs
}
const result = await uut.start() const result = await uut.start()
// console.log('result: ', result)
// Assert properties of the instance are set. // Assert properties of the instance are set.
assert.equal(uut.isReady, true) assert.equal(uut.isReady, true)
@@ -71,8 +73,9 @@ describe('#IPFS-adapter', () => {
it('should catch and throw an error', async () => { it('should catch and throw an error', async () => {
try { try {
// Force an error // Force an error by making getSeed throw, which will cause
sandbox.stub(uut, 'createNode').rejects(new Error('test error')) // CreateHeliaNode to fail during start.
sandbox.stub(uut, 'getSeed').rejects(new Error('test error'))
await uut.start() await uut.start()
assert.fail('Unexpected code path.') assert.fail('Unexpected code path.')
@@ -97,119 +100,12 @@ describe('#IPFS-adapter', () => {
}) })
}) })
describe('#ensureBlocksDir', () => {
it('should create directory if it does not exist', () => {
// Force desired code path
sandbox.stub(uut.fs, 'existsSync').returns(false)
sandbox.stub(uut.fs, 'mkdirSync').returns(true)
const result = uut.ensureBlocksDir()
assert.equal(result, true)
})
it('should report and throw errors', () => {
// Force an error
sandbox.stub(uut.fs, 'existsSync').throws(new Error('test error'))
try {
uut.ensureBlocksDir()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
})
describe('#createNode', () => {
it('should report and throw errors', async () => {
// Force an error
sandbox.stub(uut, 'createLibp2p').rejects(new Error('test error'))
try {
await uut.createNode()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should create an IPFS node from Helia', async () => {
uut.config.isCircuitRelay = false
const result = await uut.createNode()
// console.log('result: ', result)
// Assert the returned IPFS node has expected properties
assert.property(result, 'libp2p')
assert.property(result, 'blockstore')
// Stop the IPFS node
await result.stop()
})
it('should create a Circuit Relay if configured', async () => {
uut.config.isCircuitRelay = true
const result = await uut.createNode()
// Assert the returned IPFS node has expected properties
assert.property(result, 'libp2p')
assert.property(result, 'blockstore')
// Stop the IPFS node
await result.stop()
})
it('should create a new private key on first run', async () => {
uut.config.isCircuitRelay = false
// Mock dependencies and force desired code path.
// const beenCalled = false
// sandbox.stub(uut, 'getKeychain').resolves({
// exportPeerId: async () => {
// if (!beenCalled) {
// beenCalled = true
// throw new Error('test error')
// }
// return peerIdFromString('12D3KooWSXF1PnEfiA8bCG8SJduCvzdwHtvhVPK4WC6zzDoto2XP')
// },
// createKey: async () => {}
// })
sandbox.stub(uut, 'createLibp2p').resolves()
sandbox.stub(uut, 'createHelia').resolves({})
const result = await uut.createNode()
// console.log('result: ', result)
assert.property(result, 'fs')
})
it('should not use circuit relay when CONNECT_PREF is set to direct', async () => {
process.env.CONNECT_PREF = 'direct'
uut.config.isCircuitRelay = false
const result = await uut.createNode()
// console.log('result: ', result)
delete process.env.CONNECT_PREF
// Assert the returned IPFS node has expected properties
assert.property(result, 'libp2p')
assert.property(result, 'blockstore')
// Stop the IPFS node
await result.stop()
})
})
describe('#getSeed', () => { describe('#getSeed', () => {
it('should read the seed from the JSON file', async () => { it('should read the seed from the JSON file', async () => {
// Mock dependencies and force desired code path // Mock dependencies and force desired code path
sandbox.stub(uut.jsonFiles, 'readJSON').resolves('12345678') sandbox.stub(uut.jsonFiles, 'readJSON').resolves('12345678')
const result = await uut.getSeed() const result = await uut.getSeed()
// console.log('result: ', result)
assert.isString(result) assert.isString(result)
}) })
@@ -220,7 +116,6 @@ describe('#IPFS-adapter', () => {
sandbox.stub(uut.jsonFiles, 'writeJSON').resolves() sandbox.stub(uut.jsonFiles, 'writeJSON').resolves()
const result = await uut.getSeed() const result = await uut.getSeed()
// console.log('result: ', result)
assert.isString(result) assert.isString(result)
}) })
+11 -2
View File
@@ -10,15 +10,24 @@ describe('#config', () => {
before(() => { before(() => {
// Backup the current environment setting. // Backup the current environment setting.
currentEnv = process.env.CONSUMER_ENV currentEnv = process.env.CONSUMER_ENV
// Clear CONSUMER_ENV for the first test to ensure default behavior
delete process.env.CONSUMER_ENV
}) })
after(() => { after(() => {
// Restore the environment setting before starting these tests. // Restore the environment setting before starting these tests.
process.env.CONSUMER_ENV = currentEnv if (currentEnv) {
process.env.CONSUMER_ENV = currentEnv
} else {
delete process.env.CONSUMER_ENV
}
}) })
it('Should return development environment config by default', async () => { it('Should return development environment config by default', async () => {
const importedConfig = await import('../../../config/index.js') // Ensure CONSUMER_ENV is not set for this test
delete process.env.CONSUMER_ENV
const importedConfig = await import('../../../config/index.js?foo=bar0')
const config = importedConfig.default const config = importedConfig.default
// console.log('config: ', config) // console.log('config: ', config)
+1 -1
View File
@@ -17,7 +17,7 @@ class MockBchWallet {
this.walletInfoPromise = true; this.walletInfoPromise = true;
this.walletInfo = mockWallet; this.walletInfo = mockWallet;
this.initialize = async () => {} this.initialize = async () => {}
this.bchjs = new BCHJS(); this.bchjs = new BCHJS({ restURL: 'https://free-bch.fullstack.cash/' });
this.burnTokens = async () => { this.burnTokens = async () => {
return { success: true, txid: 'txid' }; return { success: true, txid: 'txid' };
}; };
+1 -1
View File
@@ -1,6 +1,6 @@
import mongoose from 'mongoose' import mongoose from 'mongoose'
import config from '../../config/index.js' import config from '../../config/index.js'
import User from '../../src/models/users.js' import User from '../../src/adapters/localdb/models/users.js'
async function getUsers () { async function getUsers () {
// Connect to the Mongo Database. // Connect to the Mongo Database.
+30
View File
@@ -0,0 +1,30 @@
import mongoose from 'mongoose'
import config from '../../config/index.js'
import User from '../../src/adapters/localdb/models/users.js'
async function getUsers () {
// Connect to the Mongo Database.
mongoose.Promise = global.Promise
mongoose.set('useCreateIndex', true) // Stop deprecation warning.
await mongoose.connect(
config.database,
{ useNewUrlParser: true, useUnifiedTopology: true }
)
// Find the user by email.
const user = await User.findOne({
email: 'test@test.com'
}, '-password')
// Update the users password
// user.password = 'newpassword'
// Change the user to an admin
// user.type = 'admin'
// Save the changes to the database.
await user.save()
mongoose.connection.close()
}
getUsers()