feat(helia v5): Generating and saving new private key

This commit is contained in:
Chris Troutner
2025-02-12 14:58:11 -07:00
parent 4d25681c51
commit 82f5552836
4 changed files with 91 additions and 192 deletions
+13 -102
View File
@@ -16,7 +16,7 @@
"@libp2p/bootstrap": "11.0.21",
"@libp2p/circuit-relay-v2": "3.1.11",
"@libp2p/identify": "3.0.18",
"@libp2p/keychain": "4.1.5",
"@libp2p/keychain": "5.0.14",
"@libp2p/logger": "5.1.8",
"@libp2p/tcp": "10.0.18",
"@libp2p/webrtc": "5.0.27",
@@ -3457,23 +3457,6 @@
"uint8arrays": "^5.1.0"
}
},
"node_modules/@libp2p/auto-tls/node_modules/@libp2p/keychain": {
"version": "5.0.14",
"resolved": "http://94.130.170.209:4873/@libp2p%2fkeychain/-/keychain-5.0.14.tgz",
"integrity": "sha512-WTxHrLu8Dx8DNmVTOvlYtODQVsFNPgjL/tmyp61HwhBqKLqeEefH5BeU7W/W3YDlnwcL0JaUi5tIdMeu2qwRLw==",
"license": "Apache-2.0 OR MIT",
"dependencies": {
"@libp2p/crypto": "^5.0.11",
"@libp2p/interface": "^2.5.0",
"@noble/hashes": "^1.6.1",
"asn1js": "^3.0.5",
"interface-datastore": "^8.3.1",
"merge-options": "^3.0.4",
"multiformats": "^13.3.1",
"sanitize-filename": "^1.6.3",
"uint8arrays": "^5.1.0"
}
},
"node_modules/@libp2p/autonat": {
"version": "2.0.19",
"resolved": "http://94.130.170.209:4873/@libp2p%2fautonat/-/autonat-2.0.19.tgz",
@@ -3546,23 +3529,6 @@
"interface-datastore": "^8.3.1"
}
},
"node_modules/@libp2p/config/node_modules/@libp2p/keychain": {
"version": "5.0.14",
"resolved": "http://94.130.170.209:4873/@libp2p%2fkeychain/-/keychain-5.0.14.tgz",
"integrity": "sha512-WTxHrLu8Dx8DNmVTOvlYtODQVsFNPgjL/tmyp61HwhBqKLqeEefH5BeU7W/W3YDlnwcL0JaUi5tIdMeu2qwRLw==",
"license": "Apache-2.0 OR MIT",
"dependencies": {
"@libp2p/crypto": "^5.0.11",
"@libp2p/interface": "^2.5.0",
"@noble/hashes": "^1.6.1",
"asn1js": "^3.0.5",
"interface-datastore": "^8.3.1",
"merge-options": "^3.0.4",
"multiformats": "^13.3.1",
"sanitize-filename": "^1.6.3",
"uint8arrays": "^5.1.0"
}
},
"node_modules/@libp2p/crypto": {
"version": "5.0.11",
"resolved": "http://94.130.170.209:4873/@libp2p%2fcrypto/-/crypto-5.0.11.tgz",
@@ -3702,59 +3668,19 @@
}
},
"node_modules/@libp2p/keychain": {
"version": "4.1.5",
"resolved": "http://94.130.170.209:4873/@libp2p%2fkeychain/-/keychain-4.1.5.tgz",
"integrity": "sha512-teDH0WbQ4mMSBCtOxV5ZoPltTj13xQiuOV27rzarRNYf4jRopPn2BU7k2c5qOFZ0VaCIvMAd1DT2Q8kfjcT4cw==",
"version": "5.0.14",
"resolved": "http://94.130.170.209:4873/@libp2p%2fkeychain/-/keychain-5.0.14.tgz",
"integrity": "sha512-WTxHrLu8Dx8DNmVTOvlYtODQVsFNPgjL/tmyp61HwhBqKLqeEefH5BeU7W/W3YDlnwcL0JaUi5tIdMeu2qwRLw==",
"license": "Apache-2.0 OR MIT",
"dependencies": {
"@libp2p/crypto": "^4.1.9",
"@libp2p/interface": "^1.7.0",
"@libp2p/peer-id": "^4.2.4",
"interface-datastore": "^8.2.11",
"merge-options": "^3.0.4",
"multiformats": "^13.1.0",
"sanitize-filename": "^1.6.3",
"uint8arrays": "^5.1.0"
}
},
"node_modules/@libp2p/keychain/node_modules/@libp2p/crypto": {
"version": "4.1.9",
"resolved": "http://94.130.170.209:4873/@libp2p%2fcrypto/-/crypto-4.1.9.tgz",
"integrity": "sha512-8Cf2VKh0uC/rQLvTLSloIOMqUvf4jsSTHXgjWQRf47lDNJlNNI0wSv2S6gakT72GZsRV/jCjYwKPqRlsa5S0iA==",
"license": "Apache-2.0 OR MIT",
"dependencies": {
"@libp2p/interface": "^1.7.0",
"@noble/curves": "^1.4.0",
"@noble/hashes": "^1.4.0",
"@libp2p/crypto": "^5.0.11",
"@libp2p/interface": "^2.5.0",
"@noble/hashes": "^1.6.1",
"asn1js": "^3.0.5",
"multiformats": "^13.1.0",
"protons-runtime": "^5.4.0",
"uint8arraylist": "^2.4.8",
"uint8arrays": "^5.1.0"
}
},
"node_modules/@libp2p/keychain/node_modules/@libp2p/interface": {
"version": "1.7.0",
"resolved": "http://94.130.170.209:4873/@libp2p%2finterface/-/interface-1.7.0.tgz",
"integrity": "sha512-/zFyaIaIGW0aihhsH7/93vQdpWInUzFocxF11RO/029Y6h0SVjs24HHbils+DqaFDTqN+L7oNlBx2rM2MnmTjA==",
"license": "Apache-2.0 OR MIT",
"dependencies": {
"@multiformats/multiaddr": "^12.2.3",
"it-pushable": "^3.2.3",
"it-stream-types": "^2.0.1",
"multiformats": "^13.1.0",
"progress-events": "^1.0.0",
"uint8arraylist": "^2.4.8"
}
},
"node_modules/@libp2p/keychain/node_modules/@libp2p/peer-id": {
"version": "4.2.4",
"resolved": "http://94.130.170.209:4873/@libp2p%2fpeer-id/-/peer-id-4.2.4.tgz",
"integrity": "sha512-mvvsVxt4HkF14BrTNKbqr14VObW+KBJBWu1Oe6BFCoDttGMQLaI+PdduE1r6Tquntv5IONBqoITgD7ow5dQ+vQ==",
"license": "Apache-2.0 OR MIT",
"dependencies": {
"@libp2p/interface": "^1.7.0",
"multiformats": "^13.1.0",
"interface-datastore": "^8.3.1",
"merge-options": "^3.0.4",
"multiformats": "^13.3.1",
"sanitize-filename": "^1.6.3",
"uint8arrays": "^5.1.0"
}
},
@@ -11647,23 +11573,6 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/helia/node_modules/@libp2p/keychain": {
"version": "5.0.14",
"resolved": "http://94.130.170.209:4873/@libp2p%2fkeychain/-/keychain-5.0.14.tgz",
"integrity": "sha512-WTxHrLu8Dx8DNmVTOvlYtODQVsFNPgjL/tmyp61HwhBqKLqeEefH5BeU7W/W3YDlnwcL0JaUi5tIdMeu2qwRLw==",
"license": "Apache-2.0 OR MIT",
"dependencies": {
"@libp2p/crypto": "^5.0.11",
"@libp2p/interface": "^2.5.0",
"@noble/hashes": "^1.6.1",
"asn1js": "^3.0.5",
"interface-datastore": "^8.3.1",
"merge-options": "^3.0.4",
"multiformats": "^13.3.1",
"sanitize-filename": "^1.6.3",
"uint8arrays": "^5.1.0"
}
},
"node_modules/hermes-estree": {
"version": "0.25.1",
"resolved": "http://94.130.170.209:4873/hermes-estree/-/hermes-estree-0.25.1.tgz",
@@ -15554,6 +15463,7 @@
"resolved": "http://94.130.170.209:4873/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"license": "MIT",
"peer": true,
"bin": {
"mkdirp": "bin/cmd.js"
},
@@ -24483,6 +24393,7 @@
"resolved": "http://94.130.170.209:4873/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
"integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
"license": "ISC",
"peer": true,
"dependencies": {
"imurmurhash": "^0.1.4",
"signal-exit": "^3.0.7"
+1 -1
View File
@@ -31,7 +31,7 @@
"@libp2p/bootstrap": "11.0.21",
"@libp2p/circuit-relay-v2": "3.1.11",
"@libp2p/identify": "3.0.18",
"@libp2p/keychain": "4.1.5",
"@libp2p/keychain": "5.0.14",
"@libp2p/logger": "5.1.8",
"@libp2p/tcp": "10.0.18",
"@libp2p/webrtc": "5.0.27",
+37 -49
View File
@@ -28,8 +28,9 @@ import { publicIpv4 } from 'public-ip'
import { multiaddr } from '@multiformats/multiaddr'
// import { webRTC } from '@libp2p/webrtc'
import { keychain } from '@libp2p/keychain'
import { defaultLogger } from '@libp2p/logger'
import { unixfs } from '@helia/unixfs'
import { generateKeyPairFromSeed } from '@libp2p/crypto/keys'
import crypto from 'crypto'
// Local libraries
import config from '../../../config/index.js'
@@ -64,8 +65,7 @@ class IpfsAdapter {
this.createNode = this.createNode.bind(this)
this.stop = this.stop.bind(this)
this.ensureBlocksDir = this.ensureBlocksDir.bind(this)
this.getSeed = this.getSeed.bind(this)
this.getKeychain = this.getKeychain.bind(this)
this.getPrivateKey = this.getPrivateKey.bind(this)
}
// Start an IPFS node.
@@ -111,19 +111,6 @@ class IpfsAdapter {
}
}
async getKeychain (datastore) {
const keychainInit = {
pass: await this.getSeed()
}
const chain = this.keychain(keychainInit)({
datastore,
logger: defaultLogger()
})
return chain
}
// This function creates an IPFS node using Helia.
// It returns the node as an object.
async createNode () {
@@ -132,25 +119,9 @@ class IpfsAdapter {
const blockstore = new FsBlockstore(`${IPFS_DIR}/blockstore`)
const datastore = new FsDatastore(`${IPFS_DIR}/datastore`)
// const keychainInit = {
// pass: await this.getSeed()
// }
// Create an identity
let peerId
// console.log('this.keychain: ', this.keychain)
// const chain = this.keychain(keychainInit)({
// datastore,
// logger: defaultLogger()
// })
const chain = await this.getKeychain(datastore)
try {
peerId = await chain.exportPeerId('myKey')
} catch (err) {
await chain.createKey('myKey', 'Ed25519', 4096)
peerId = await chain.exportPeerId('myKey')
}
console.log('peerId: ', peerId)
// Get the private key.
const privateKey = await this.getPrivateKey()
// console.log('privateKey: ', privateKey)
// Configure services
const services = {
@@ -188,7 +159,8 @@ class IpfsAdapter {
// libp2p is the networking layer that underpins Helia
const libp2p = await this.createLibp2p({
peerId,
// peerId,
privateKey,
datastore,
addresses: {
listen: [
@@ -245,6 +217,8 @@ class IpfsAdapter {
!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
@@ -254,30 +228,44 @@ class IpfsAdapter {
}
}
// 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.
async getSeed () {
// Get the private key from disk, or generate a new one and save it,
// if it doesn't exist.
async getPrivateKey () {
try {
let seed
const filename = `${IPFS_DIR}/seed.json`
const filename = `${IPFS_DIR}/privkey.json`
let privKeyHex
try {
// Try to read the JSON file containing the seed.
seed = await this.jsonFiles.readJSON(filename)
privKeyHex = await this.jsonFiles.readJSON(filename)
// console.log('saved privKeyHex: ', privKeyHex)
} catch (err) {
const seedNum = Math.floor(Math.random() * 1000000000000000000000)
seed = seedNum.toString()
// 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(seed, filename)
await this.jsonFiles.writeJSON(privKeyHex, filename)
}
// console.log('getSeed() seed: ', seed)
// Convert the saved hex string to a buffer.
const privKeyBuf = Buffer.from(privKeyHex, 'hex')
return seed
// 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/getSeed()')
console.error('Error in adapters/ipfs/ipfs.js/getPrivateKey(): ', err)
throw err
}
}
+46 -46
View File
@@ -6,7 +6,7 @@
import { assert } from 'chai'
import sinon from 'sinon'
import cloneDeep from 'lodash.clonedeep'
import { peerIdFromString } from '@libp2p/peer-id'
// import { peerIdFromString } from '@libp2p/peer-id'
// Local libraries
import IPFSLib from '../../../src/adapters/ipfs/ipfs.js'
@@ -165,17 +165,17 @@ describe('#IPFS-adapter', () => {
uut.config.isCircuitRelay = false
// Mock dependencies and force desired code path.
let beenCalled = false
sandbox.stub(uut, 'getKeychain').resolves({
exportPeerId: async () => {
if (!beenCalled) {
beenCalled = true
throw new Error('test error')
}
return peerIdFromString('12D3KooWSXF1PnEfiA8bCG8SJduCvzdwHtvhVPK4WC6zzDoto2XP')
},
createKey: async () => {}
})
// 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({})
@@ -203,40 +203,40 @@ describe('#IPFS-adapter', () => {
})
})
describe('#getSeed', () => {
it('should read the seed from the JSON file', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut.jsonFiles, 'readJSON').resolves('12345678')
// describe('#getSeed', () => {
// it('should read the seed from the JSON file', async () => {
// // Mock dependencies and force desired code path
// sandbox.stub(uut.jsonFiles, 'readJSON').resolves('12345678')
const result = await uut.getSeed()
// console.log('result: ', result)
// const result = await uut.getSeed()
// // console.log('result: ', result)
assert.isString(result)
})
it('should generate a new seed if the JSON file is not found', async () => {
// Mock dependencies and force desired code path
sandbox.stub(uut.jsonFiles, 'readJSON').rejects(new Error('test error'))
sandbox.stub(uut.jsonFiles, 'writeJSON').resolves()
const result = await uut.getSeed()
// console.log('result: ', result)
assert.isString(result)
})
it('should catch, report, and throw errors', async () => {
try {
// Force an error
sandbox.stub(uut.jsonFiles, 'readJSON').rejects(new Error('test error'))
sandbox.stub(uut.jsonFiles, 'writeJSON').rejects(new Error('test error'))
await uut.getSeed()
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
})
// assert.isString(result)
// })
// it('should generate a new seed if the JSON file is not found', async () => {
// // Mock dependencies and force desired code path
// sandbox.stub(uut.jsonFiles, 'readJSON').rejects(new Error('test error'))
// sandbox.stub(uut.jsonFiles, 'writeJSON').resolves()
// const result = await uut.getSeed()
// // console.log('result: ', result)
// assert.isString(result)
// })
// it('should catch, report, and throw errors', async () => {
// try {
// // Force an error
// sandbox.stub(uut.jsonFiles, 'readJSON').rejects(new Error('test error'))
// sandbox.stub(uut.jsonFiles, 'writeJSON').rejects(new Error('test error'))
// await uut.getSeed()
// assert.fail('Unexpected code path')
// } catch (err) {
// assert.include(err.message, 'test error')
// }
// })
// })
})