jupiter-fs
Advanced tools
Comparing version 0.0.11 to 0.1.0
@@ -1,1 +0,1 @@ | ||
export default function JupiterFs({ server, address, passphrase, encryptSecret, feeNQT, }: any): any; | ||
export default function JupiterFs({ server, address, passphrase, encryptSecret, feeNQT, minimumFndrAccountBalance, minimumUserAccountBalance }: any): any; |
@@ -30,5 +30,13 @@ "use strict"; | ||
const jupiter_node_sdk_1 = __importStar(require("jupiter-node-sdk")); | ||
function JupiterFs({ server, address, passphrase, encryptSecret, feeNQT, }) { | ||
const jupServer = server || 'https://jpr.gojupiter.tech'; | ||
const zlib_1 = __importDefault(require("zlib")); | ||
function JupiterFs({ server, address, passphrase, encryptSecret, feeNQT, minimumFndrAccountBalance, minimumUserAccountBalance }) { | ||
// const jupServer = server || 'https://fs.jup.io' | ||
const jupServer = server || ''; | ||
feeNQT = feeNQT || 400; | ||
// Quantity to found the binary client when doesnt have enought founds | ||
minimumFndrAccountBalance = minimumFndrAccountBalance || 1000000; | ||
minimumUserAccountBalance = minimumUserAccountBalance || 2000000; | ||
// Chunk size to split the file to upload | ||
// Max lengh in Jupiter is 43008 bytes per encrypted message | ||
const CHUNK_SIZE_PATTERN = /.{1,40000}/g; | ||
return { | ||
@@ -43,2 +51,4 @@ key: `jupiter-fs`, | ||
feeNQT, | ||
minimumFndrAccountBalance, | ||
minimumUserAccountBalance | ||
}), | ||
@@ -58,2 +68,4 @@ binaryClient: null, | ||
feeNQT, | ||
minimumFndrAccountBalance: this.binaryClient.minimumFndrAccountBalance, | ||
minimumUserAccountBalance: this.binaryClient.minimumUserAccountBalance | ||
}; | ||
@@ -79,14 +91,30 @@ } | ||
await this.checkAndFundAccount(addy.address); | ||
this.binaryClient = jupiter_node_sdk_1.default({ ...addy, server: jupServer, feeNQT }); | ||
this.binaryClient = jupiter_node_sdk_1.default({ ...addy, | ||
server: jupServer, | ||
feeNQT, | ||
minimumFndrAccountBalance, | ||
minimumUserAccountBalance }); | ||
return addy; | ||
}, | ||
async checkAndFundAccount(targetAddress) { | ||
// Get balance for binary client | ||
const balanceJup = await this.client.getBalance(targetAddress); | ||
if (!balanceJup || | ||
if ( | ||
// if binary client doesnt have money or is less than minimumFndrAccountBalance | ||
// then send money to support file upload | ||
!balanceJup || | ||
new bignumber_js_1.default(balanceJup).lt(new bignumber_js_1.default(this.client.nqtToJup(this.client.config.minimumFndrAccountBalance)).div(2))) { | ||
// send money to the binary client to pay fees for transactions | ||
await this.client.sendMoney(targetAddress); | ||
} | ||
}, | ||
/** | ||
* Get the address for the binary account used to upload files | ||
* @returns | ||
*/ | ||
async getBinaryAddress() { | ||
// Get all the transactions for the main jupiter account | ||
const allTxns = await this.client.getAllTransactions(); | ||
// for each transaction, check if contains the jupiter-fs metaDataKey and | ||
// decrypt the chuncked transactions | ||
const binaryAccountInfo = (await Promise.all(allTxns.map(async (txn) => { | ||
@@ -96,2 +124,3 @@ try { | ||
let data = JSON.parse(await this.client.decrypt(decryptedMessage)); | ||
// tx with jupiter-fs-meta:true contains info related to the binary client | ||
if (!data[this.metaDataKey]) | ||
@@ -133,8 +162,20 @@ return false; | ||
}, | ||
/** | ||
* Push a file into the Jupiter blockchain | ||
* The file is splitted into chunks of CHUNK_SIZE_PATTERN | ||
* and pushed by the binary client | ||
* @param name | ||
* @param data | ||
* @param errorCallback | ||
* @returns | ||
*/ | ||
async writeFile(name, data, errorCallback) { | ||
await this.getOrCreateBinaryAddress(); | ||
const chunks = data.toString('base64').match(/.{1,1000}/g); | ||
// compress the binary data before to convert to base64 | ||
const encodedFileData = zlib_1.default.deflateSync(Buffer.from(data)).toString('base64'); | ||
const chunks = encodedFileData.match(CHUNK_SIZE_PATTERN); | ||
assert_1.default(chunks, `we couldn't split the data into chunks`); | ||
const dataTxns = await Promise.all(chunks.map(async (str) => { | ||
const { transaction } = await exponentialBackoff(async () => { | ||
// check if the binarclient have enought found for the transaction | ||
await this.checkAndFundAccount(this.binaryClient.address); | ||
@@ -170,19 +211,25 @@ return await this.binaryClient.storeRecord({ | ||
await this.getOrCreateBinaryAddress(); | ||
// search first in the unconfirmed transactions | ||
let txns = await this.binaryClient.getAllUnconfirmedTransactions(); | ||
const files = await this.ls(); | ||
const targetFile = files.find((t) => (id && id === t.id) || t.fileName === name); | ||
if (!targetFile) { | ||
// if not found, search in the confirmed transactions | ||
txns = await this.binaryClient.getAllConfirmedTransactions(); | ||
const files = await this.ls(); | ||
const targetFile = files.find((t) => (id && id === t.id) || t.fileName === name); | ||
} | ||
assert_1.default(targetFile, 'target file was not found'); | ||
// decrypt the transactions info with the list of txIds where is stored the file | ||
const dataTxns = JSON.parse(await this.client.decrypt(targetFile.txns)); | ||
const readable = new stream_1.Readable(); | ||
/** | ||
* Get the base64 chunks of the image | ||
* @param readableStream | ||
* @returns | ||
*/ | ||
const getBase64Strings = async (readableStream) => { | ||
const allBase64Strings = await Promise.all(dataTxns.map(async (txnId) => { | ||
const { data } = await this.binaryClient.request('post', '/nxt', { | ||
params: { | ||
requestType: 'readMessage', | ||
secretPhrase: encryptSecret || this.binaryClient.passphrase, | ||
transaction: txnId, | ||
}, | ||
}); | ||
if (data.errorCode > 0) | ||
throw new Error(JSON.stringify(data)); | ||
const jsonWithData = await this.binaryClient.decrypt(data.decryptedMessage); | ||
// Decrypt the message and parse the json chunk | ||
const getBase64Chunk = async (decryptedMessage) => { | ||
const jsonWithData = await this.binaryClient.decrypt(decryptedMessage); | ||
const base64Chunk = JSON.parse(jsonWithData).data; | ||
@@ -192,2 +239,22 @@ if (readableStream) | ||
return base64Chunk; | ||
}; | ||
// Get the transaction info for each txId of the file | ||
const allBase64Strings = await Promise.all(dataTxns.map(async (txnId) => { | ||
try { | ||
const { data } = await this.binaryClient.request('post', '/nxt', { | ||
params: { | ||
requestType: 'readMessage', | ||
secretPhrase: encryptSecret || this.binaryClient.passphrase, | ||
transaction: txnId, | ||
}, | ||
}); | ||
if (data.errorCode > 0) { | ||
throw new Error(JSON.stringify(data)); | ||
} | ||
// decrypt and decode the chunk | ||
return await getBase64Chunk(data.decryptedMessage); | ||
} | ||
catch (err) { | ||
throw new Error(`target file was not found ` + JSON.stringify(err)); | ||
} | ||
})); | ||
@@ -205,3 +272,3 @@ if (readableStream) | ||
const base64Strings = await getBase64Strings(); | ||
return Buffer.from(base64Strings.join(''), 'base64'); | ||
return zlib_1.default.inflateSync(Buffer.from(base64Strings.join(''), 'base64')); | ||
}, | ||
@@ -222,2 +289,13 @@ async getFileStream({ name, id }) { | ||
exports.default = JupiterFs; | ||
/** | ||
* Function to create a exponential backoff | ||
* if there is an error, it wait for some time and if the problems contine | ||
* the time to wait is increased in a exponentional way | ||
* @param promiseFunction | ||
* @param failureFunction | ||
* @param err | ||
* @param totalAllowedBackoffTries | ||
* @param backoffAttempt | ||
* @returns | ||
*/ | ||
async function exponentialBackoff(promiseFunction, failureFunction = () => { }, err = null, totalAllowedBackoffTries = 10, backoffAttempt = 1) { | ||
@@ -224,0 +302,0 @@ const backoffSecondsToWait = 2 + Math.pow(backoffAttempt, 2); |
export {}; |
@@ -9,2 +9,3 @@ "use strict"; | ||
const path_1 = __importDefault(require("path")); | ||
const uuid_1 = require("uuid"); | ||
const JupiterFs_1 = __importDefault(require("./JupiterFs")); | ||
@@ -17,9 +18,11 @@ /** | ||
describe('JupiterFs', function () { | ||
this.timeout(5000); | ||
// const fs = JupiterFs({ server: 'http://localhost:7876' }) | ||
assert_1.default(process.env.JUPITER_ADDRESS, 'JUPITER_ADDRESS env variable is not set'); | ||
assert_1.default(process.env.JUPITER_PASSPHRASE, 'JUPITER_PASSPHRASE env variable is not set'); | ||
// const fs = JupiterFs({ server: 'http://localhost:6876' }) | ||
const jupFs = JupiterFs_1.default({ | ||
server: 'https://jpr4.gojupiter.tech', | ||
address: '', | ||
passphrase: '', | ||
server: process.env.JUPITER_SERVER || 'http://104.131.166.136:6876/test', | ||
address: process.env.JUPITER_ADDRESS, | ||
passphrase: process.env.JUPITER_PASSPHRASE, | ||
}); | ||
const testFilename = `${uuid_1.v1()}.js`; | ||
describe('#newBinaryAddress()', function () { | ||
@@ -34,10 +37,8 @@ it(`should get a new JUP address from a passphrase`, async () => { | ||
const addy = await jupFs.getOrCreateBinaryAddress(); | ||
// console.log('ADDY', addy) | ||
assert_1.default.strictEqual(typeof addy.address === 'string', true); | ||
}); | ||
}); | ||
describe('#ls()', function () { | ||
xdescribe('#ls()', function () { | ||
it(`should fetch a list of files for a jupiter account`, async () => { | ||
const files = await jupFs.ls(); | ||
// console.log('FILES', files) | ||
assert_1.default.strictEqual(files instanceof Array, true); | ||
@@ -47,7 +48,7 @@ }); | ||
describe('#writeFile()', function () { | ||
this.timeout(10000); | ||
it(`should write a file to a jupiter account without error`, async () => { | ||
const fileData = await fs_1.default.promises.readFile(path_1.default.join(__dirname, 'JupiterFs.ts'), { encoding: null }); | ||
const res = await jupFs.writeFile('JupiterFs.ts', fileData, (err) => console.error(`Error writing file`, err)); | ||
assert_1.default.strictEqual(res.fileName, 'JupiterFs.ts'); | ||
const fileData = await fs_1.default.promises.readFile(path_1.default.join(__dirname, '../testFiles/medium.jpg'), { encoding: null }); | ||
console.log("filename " + testFilename); | ||
const res = await jupFs.writeFile(testFilename, fileData, assert_1.default.fail); | ||
assert_1.default.strictEqual(res.fileName, testFilename); | ||
assert_1.default.strictEqual(res.txns.length > 0, true); | ||
@@ -58,8 +59,10 @@ }); | ||
it(`should get the binary data for a file specified`, async () => { | ||
const fileData = await jupFs.getFile({ name: `JupiterFs.ts` }); | ||
await fs_1.default.promises.writeFile(path_1.default.join(__dirname, 'JupiterFs.ts'), fileData); | ||
console.log("filename " + testFilename); | ||
const fileData = await jupFs.getFile({ name: testFilename }); | ||
const origFileData = await fs_1.default.promises.readFile(path_1.default.join(__dirname, '../testFiles/medium.jpg'), 'utf-8'); | ||
assert_1.default.strictEqual(fileData instanceof Buffer, true); | ||
assert_1.default.strictEqual(fileData.length > 0, true); | ||
assert_1.default.strictEqual(origFileData, fileData.toString('utf-8')); | ||
}); | ||
}); | ||
}); |
{ | ||
"name": "jupiter-fs", | ||
"version": "0.0.11", | ||
"version": "0.1.0", | ||
"description": "A small file system implementation for the Jupiter blockchain.", | ||
@@ -8,3 +8,3 @@ "main": "./dist/JupiterFs.js", | ||
"type": "git", | ||
"url": "https://github.com/whatl3y/jupiter-fs" | ||
"url": "https://github.com/jupiter-project/jupiter-fs" | ||
}, | ||
@@ -27,3 +27,3 @@ "scripts": { | ||
"dependencies": { | ||
"jupiter-node-sdk": "^0.1.3", | ||
"jupiter-node-sdk": "^0.2.1", | ||
"uuid": "^8.3.2" | ||
@@ -30,0 +30,0 @@ }, |
@@ -0,0 +0,0 @@ # jupiter-fs |
@@ -13,4 +13,2 @@ import assert from 'assert' | ||
describe('JupiterFs', function() { | ||
this.timeout(10000) | ||
assert(process.env.JUPITER_ADDRESS, 'JUPITER_ADDRESS env variable is not set') | ||
@@ -22,5 +20,5 @@ assert( | ||
// const fs = JupiterFs({ server: 'http://localhost:7876' }) | ||
// const fs = JupiterFs({ server: 'http://localhost:6876' }) | ||
const jupFs = JupiterFs({ | ||
server: process.env.JUPITER_SERVER || 'https://jpr4.gojupiter.tech', | ||
server: process.env.JUPITER_SERVER || 'http://104.131.166.136:6876/test', | ||
address: process.env.JUPITER_ADDRESS, | ||
@@ -45,3 +43,3 @@ passphrase: process.env.JUPITER_PASSPHRASE, | ||
describe('#ls()', function() { | ||
xdescribe('#ls()', function() { | ||
it(`should fetch a list of files for a jupiter account`, async () => { | ||
@@ -56,5 +54,6 @@ const files = await jupFs.ls() | ||
const fileData = await fs.promises.readFile( | ||
path.join(__dirname, 'JupiterFs.ts'), | ||
path.join(__dirname, '../testFiles/medium.jpg'), | ||
{ encoding: null } | ||
) | ||
console.log("filename " + testFilename); | ||
const res = await jupFs.writeFile(testFilename, fileData, assert.fail) | ||
@@ -68,5 +67,6 @@ assert.strictEqual(res.fileName, testFilename) | ||
it(`should get the binary data for a file specified`, async () => { | ||
console.log("filename " + testFilename); | ||
const fileData = await jupFs.getFile({ name: testFilename }) | ||
const origFileData = await fs.promises.readFile( | ||
path.join(__dirname, 'JupiterFs.ts'), | ||
path.join(__dirname, '../testFiles/medium.jpg'), | ||
'utf-8' | ||
@@ -73,0 +73,0 @@ ) |
@@ -6,2 +6,3 @@ import assert from 'assert' | ||
import JupiterClient, { generatePassphrase } from 'jupiter-node-sdk' | ||
import zlib from 'zlib' | ||
@@ -14,6 +15,16 @@ export default function JupiterFs({ | ||
feeNQT, | ||
minimumFndrAccountBalance, | ||
minimumUserAccountBalance | ||
}: any): any { | ||
const jupServer = server || 'https://jpr.gojupiter.tech' | ||
// const jupServer = server || 'https://fs.jup.io' | ||
const jupServer = server || '' | ||
feeNQT = feeNQT || 400 | ||
// Quantity to found the binary client when doesnt have enought founds | ||
minimumFndrAccountBalance = minimumFndrAccountBalance || 1000000 | ||
minimumUserAccountBalance = minimumUserAccountBalance || 2000000 | ||
// Chunk size to split the file to upload | ||
// Max lengh in Jupiter is 43008 bytes per encrypted message | ||
const CHUNK_SIZE_PATTERN = /.{1,40000}/g; | ||
return { | ||
@@ -28,2 +39,4 @@ key: `jupiter-fs`, | ||
feeNQT, | ||
minimumFndrAccountBalance, | ||
minimumUserAccountBalance | ||
}), | ||
@@ -44,2 +57,4 @@ binaryClient: null, | ||
feeNQT, | ||
minimumFndrAccountBalance: this.binaryClient.minimumFndrAccountBalance, | ||
minimumUserAccountBalance: this.binaryClient.minimumUserAccountBalance | ||
} | ||
@@ -72,3 +87,7 @@ } | ||
await this.checkAndFundAccount(addy.address) | ||
this.binaryClient = JupiterClient({ ...addy, server: jupServer, feeNQT }) | ||
this.binaryClient = JupiterClient({ ...addy, | ||
server: jupServer, | ||
feeNQT, | ||
minimumFndrAccountBalance, | ||
minimumUserAccountBalance }) | ||
return addy | ||
@@ -78,4 +97,7 @@ }, | ||
async checkAndFundAccount(targetAddress: string) { | ||
// Get balance for binary client | ||
const balanceJup = await this.client.getBalance(targetAddress) | ||
if ( | ||
// if binary client doesnt have money or is less than minimumFndrAccountBalance | ||
// then send money to support file upload | ||
!balanceJup || | ||
@@ -88,2 +110,3 @@ new BigNumber(balanceJup).lt( | ||
) { | ||
// send money to the binary client to pay fees for transactions | ||
await this.client.sendMoney(targetAddress) | ||
@@ -93,4 +116,11 @@ } | ||
/** | ||
* Get the address for the binary account used to upload files | ||
* @returns | ||
*/ | ||
async getBinaryAddress() { | ||
// Get all the transactions for the main jupiter account | ||
const allTxns = await this.client.getAllTransactions() | ||
// for each transaction, check if contains the jupiter-fs metaDataKey and | ||
// decrypt the chuncked transactions | ||
const binaryAccountInfo: any = ( | ||
@@ -104,4 +134,5 @@ await Promise.all( | ||
let data = JSON.parse(await this.client.decrypt(decryptedMessage)) | ||
// tx with jupiter-fs-meta:true contains info related to the binary client | ||
if (!data[this.metaDataKey]) return false | ||
return { transaction: txn.transaction, ...data } | ||
@@ -122,2 +153,3 @@ } catch (err) { | ||
) | ||
return Object.values(binaryAccountInfo).find((r: any) => !r.isDeleted) | ||
@@ -156,2 +188,11 @@ }, | ||
/** | ||
* Push a file into the Jupiter blockchain | ||
* The file is splitted into chunks of CHUNK_SIZE_PATTERN | ||
* and pushed by the binary client | ||
* @param name | ||
* @param data | ||
* @param errorCallback | ||
* @returns | ||
*/ | ||
async writeFile( | ||
@@ -162,4 +203,8 @@ name: string, | ||
) { | ||
await this.getOrCreateBinaryAddress() | ||
const chunks = data.toString('base64').match(/.{1,1000}/g) | ||
await this.getOrCreateBinaryAddress() | ||
// compress the binary data before to convert to base64 | ||
const encodedFileData = zlib.deflateSync(Buffer.from(data)).toString('base64') | ||
const chunks = encodedFileData.match(CHUNK_SIZE_PATTERN) | ||
assert(chunks, `we couldn't split the data into chunks`) | ||
@@ -170,2 +215,3 @@ | ||
const { transaction } = await exponentialBackoff(async () => { | ||
// check if the binarclient have enought found for the transaction | ||
await this.checkAndFundAccount(this.binaryClient.address) | ||
@@ -209,3 +255,5 @@ return await this.binaryClient.storeRecord({ | ||
await this.getOrCreateBinaryAddress() | ||
const unconfTxns = await this.binaryClient.getAllUnconfirmedTransactions() | ||
// search first in the unconfirmed transactions | ||
let txns = await this.binaryClient.getAllUnconfirmedTransactions() | ||
const files = await this.ls() | ||
@@ -215,9 +263,27 @@ const targetFile = files.find( | ||
) | ||
if (!targetFile){ | ||
// if not found, search in the confirmed transactions | ||
txns = await this.binaryClient.getAllConfirmedTransactions() | ||
const files = await this.ls() | ||
const targetFile = files.find( | ||
(t: any) => (id && id === t.id) || t.fileName === name | ||
) | ||
} | ||
assert(targetFile, 'target file was not found') | ||
// decrypt the transactions info with the list of txIds where is stored the file | ||
const dataTxns = JSON.parse(await this.client.decrypt(targetFile.txns)) | ||
const readable = new Readable() | ||
/** | ||
* Get the base64 chunks of the image | ||
* @param readableStream | ||
* @returns | ||
*/ | ||
const getBase64Strings = async ( | ||
readableStream?: Readable | ||
): Promise<string[]> => { | ||
// Decrypt the message and parse the json chunk | ||
const getBase64Chunk = async (decryptedMessage: string) => { | ||
@@ -231,2 +297,3 @@ const jsonWithData = await this.binaryClient.decrypt(decryptedMessage) | ||
// Get the transaction info for each txId of the file | ||
const allBase64Strings: string[] = await Promise.all( | ||
@@ -242,14 +309,11 @@ dataTxns.map(async (txnId: string) => { | ||
}) | ||
if (data.errorCode > 0) throw new Error(JSON.stringify(data)) | ||
if (data.errorCode > 0){ | ||
throw new Error(JSON.stringify(data)) | ||
} | ||
// decrypt and decode the chunk | ||
return await getBase64Chunk(data.decryptedMessage) | ||
} catch (err) { | ||
const txn = unconfTxns.find( | ||
(txn: any) => txn.transaction === txnId | ||
) | ||
if (!txn) throw new Error(`target file was not found`) | ||
const decryptedMessage = await this.binaryClient.decryptRecord( | ||
txn.attachment.encryptedMessage | ||
) | ||
return await getBase64Chunk(decryptedMessage) | ||
throw new Error(`target file was not found ` + JSON.stringify(err)) | ||
} | ||
@@ -270,3 +334,3 @@ }) | ||
const base64Strings = await getBase64Strings() | ||
return Buffer.from(base64Strings.join(''), 'base64') | ||
return zlib.inflateSync(Buffer.from(base64Strings.join(''), 'base64')) | ||
}, | ||
@@ -289,2 +353,13 @@ | ||
/** | ||
* Function to create a exponential backoff | ||
* if there is an error, it wait for some time and if the problems contine | ||
* the time to wait is increased in a exponentional way | ||
* @param promiseFunction | ||
* @param failureFunction | ||
* @param err | ||
* @param totalAllowedBackoffTries | ||
* @param backoffAttempt | ||
* @returns | ||
*/ | ||
async function exponentialBackoff( | ||
@@ -291,0 +366,0 @@ promiseFunction: any, |
@@ -0,0 +0,0 @@ { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 3 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
232775
14
840
6
1
+ Addedjupiter-node-sdk@0.2.1(transitive)
- Removedjupiter-node-sdk@0.1.3(transitive)
Updatedjupiter-node-sdk@^0.2.1