From 860464a55eb4e84c6d42ecb0d21051dfb345e254 Mon Sep 17 00:00:00 2001 From: Himank Jain Date: Thu, 27 Oct 2022 15:57:07 +0530 Subject: [PATCH 1/6] add file to app --- src/fileAPI.ts | 11 +++++++++++ tests/test.ts | 53 +++++++++++++++++++++++++++++++++++++++++++------- usage.md | 8 ++++++++ 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/src/fileAPI.ts b/src/fileAPI.ts index 6c6939f..e99143d 100644 --- a/src/fileAPI.ts +++ b/src/fileAPI.ts @@ -277,4 +277,15 @@ export class FileAPI { } return users.filter((d) => d !== ethers.constants.AddressZero) } + + @requiresLocking + private async _addFile(did:string) { + await this.setAppAddress(did) + return await makeTx(this.appAddress, this.api, this.provider, 'addFile', [did]) + } + + addFile = async (did: string) => { + const realDID = parseHex(did) + return this._addFile(realDID) + } } diff --git a/tests/test.ts b/tests/test.ts index b79d1e5..2a8c5fb 100644 --- a/tests/test.ts +++ b/tests/test.ts @@ -35,7 +35,7 @@ Below to be covered in Integration Tests -> Download (due to tus client instance) */ -function sleep (ms) { +function sleep(ms) { return new Promise((resolve) => { setTimeout(resolve, ms) }) @@ -59,7 +59,7 @@ let file // receiverInstance, const did = '0x4de0e96b0a8886e42a2c35b57df8a9d58a93b5bff655bc37a30e2ab8e29dc066' -function meta_tx_nock (reply_data) { +function meta_tx_nock(reply_data) { const nockMetaReply = async (uri, body: any) => { return reply_data ?? { data: 'dummy data', token: 'dummy token' } } @@ -72,7 +72,7 @@ function meta_tx_nock (reply_data) { .reply(200, nockMetaReply, { 'access-control-allow-headers': 'Authorization' }) } -function mock_dkg (reply_data) { +function mock_dkg(reply_data) { nock('https://dkgnode1.arcana.network:443') .defaultReplyHeaders(nockOptions) .post('/rpc') @@ -80,7 +80,7 @@ function mock_dkg (reply_data) { .reply(200, { jsonrpc: '2.0', result: { ok: true }, id: 10 }) } -async function nockSetup () { +async function nockSetup() { nock('http://localhost:9010') .defaultReplyHeaders(nockOptions) .persist() @@ -118,7 +118,7 @@ async function nockSetup () { .reply(200, {}) } -function sinonMockObjectSetup () { +function sinonMockObjectSetup() { sinon.replace(utils, 'getDKGNodes', () => [ { declaredIp: 'dkgnode1.arcana.network:443', @@ -161,7 +161,7 @@ function sinonMockObjectSetup () { sinon.replace(utils, 'getFile', () => Promise.resolve({ app: '0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9' })) } -async function mockFile () { +async function mockFile() { // file = MockFile('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.txt', 2 ** 10, 'image/txt'); // file = new File([file], "picsum_img", { type: file.type }); return new nBlob([await (await fetch('https://picsum.photos/id/872/200/300')).arrayBuffer()]) @@ -193,7 +193,7 @@ test.serial.before(async (t) => { }) // TODO: get request handler in arrays -async function createStorageInstance (wallet: Wallet, middleware?) { +async function createStorageInstance(wallet: Wallet, middleware?) { const engine = createEngine(wallet.address) if (middleware) { @@ -544,3 +544,42 @@ test.serial('Grant app permission', async (t) => { const arcanaInstance = await createStorageInstance(arcanaWallet, middleware) await t.notThrowsAsync(arcanaInstance.grantAppPermission()) }) + +test.serial("Add file to app", async (t) => { + t.plan(7) + meta_tx_nock(null) + let scope = nock(gateway) + .defaultReplyHeaders(nockOptions) + .get('/list-files/') + .query(true) + .reply(200, [{ did: did.substring(2) }], { 'access-control-allow-headers': 'Authorization' }) + .get('/files/total/') + .reply(200, { data: 1 }) + + const arcanaInstance = await createStorageInstance(arcanaWallet) + + let files:any = await arcanaInstance.myFiles() + + t.true(scope.isDone()) + t.is(files.length, 1) + t.is(files[0].did, did.substring(2)) + + const did2 = "0x4de0e96b0a8886e42a2c35b57df8a9d58a93b5bff655bc37a30e2ab8e29dc066" + + const access = await arcanaInstance.getAccess(); + await t.notThrowsAsync( access.addFile(did2)); + + scope = nock(gateway) + .defaultReplyHeaders(nockOptions) + .get('/list-files/') + .query(true) + .reply(200, [{ did: did.substring(2) }, {did: did2.substring(2)}], { 'access-control-allow-headers': 'Authorization' }) + .get('/files/total/') + .reply(200, { data: 2 }) + + files = await arcanaInstance.myFiles() + t.true(scope.isDone()) + t.is(files.length, 2) + t.is(files[1].did, did2.substring(2)) + +}) \ No newline at end of file diff --git a/usage.md b/usage.md index 97a6883..d37668c 100644 --- a/usage.md +++ b/usage.md @@ -286,4 +286,12 @@ returns `true` if required. ```js const isPermissionRequired = await storage.checkPermission() -> boolean +``` + +### Add existing file to App +Add an already uploaded file to selected app. + +```js +const access = await storage.getAccess(); +await access.addFile() ``` \ No newline at end of file From 77dbd5db820273e70b1cea6382db5c02ad6a8c48 Mon Sep 17 00:00:00 2001 From: Himank Jain Date: Thu, 27 Oct 2022 16:05:23 +0530 Subject: [PATCH 2/6] remove file usage and test --- tests/test.ts | 36 ++++++++++++++++++++++++++++++++++++ usage.md | 10 ++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/tests/test.ts b/tests/test.ts index 2a8c5fb..9cc52ac 100644 --- a/tests/test.ts +++ b/tests/test.ts @@ -582,4 +582,40 @@ test.serial("Add file to app", async (t) => { t.is(files.length, 2) t.is(files[1].did, did2.substring(2)) +}) + +test.serial("Remove file from app", async (t) => { + t.plan(6) + meta_tx_nock(null) + let scope = nock(gateway) + .defaultReplyHeaders(nockOptions) + .get('/list-files/') + .query(true) + .reply(200, [{ did: did.substring(2) }], { 'access-control-allow-headers': 'Authorization' }) + .get('/files/total/') + .reply(200, { data: 1 }) + + const arcanaInstance = await createStorageInstance(arcanaWallet) + + let files:any = await arcanaInstance.myFiles() + + t.true(scope.isDone()) + t.is(files.length, 1) + t.is(files[0].did, did.substring(2)) + + const access = await arcanaInstance.getAccess(); + await t.notThrowsAsync( access.removeFileFromApp(did)); + + scope = nock(gateway) + .defaultReplyHeaders(nockOptions) + .get('/list-files/') + .query(true) + .reply(200, [], { 'access-control-allow-headers': 'Authorization' }) + .get('/files/total/') + .reply(200, { data: 0 }) + + files = await arcanaInstance.myFiles() + t.true(scope.isDone()) + t.is(files.length, 0) + }) \ No newline at end of file diff --git a/usage.md b/usage.md index d37668c..9809997 100644 --- a/usage.md +++ b/usage.md @@ -292,6 +292,12 @@ const isPermissionRequired = await storage.checkPermission() -> boolean Add an already uploaded file to selected app. ```js -const access = await storage.getAccess(); -await access.addFile() +await storage.files.addFile() +``` + +### Remove file from App +Removes file from selected app. Does **NOT** delete from arcana storage, use `delete` for that case. + +```js +await storage.files.removeFileFromApp() ``` \ No newline at end of file From 9634dba075e7d3ca7879b72ea7953478a0e29d68 Mon Sep 17 00:00:00 2001 From: Himank Jain Date: Thu, 27 Oct 2022 16:08:22 +0530 Subject: [PATCH 3/6] lint fixes --- src/fileAPI.ts | 2 +- tests/test.ts | 505 ++++++++++++++++++++++++------------------------- tests/utils.ts | 12 +- 3 files changed, 259 insertions(+), 260 deletions(-) diff --git a/src/fileAPI.ts b/src/fileAPI.ts index e99143d..04f0da8 100644 --- a/src/fileAPI.ts +++ b/src/fileAPI.ts @@ -279,7 +279,7 @@ export class FileAPI { } @requiresLocking - private async _addFile(did:string) { + private async _addFile (did: string) { await this.setAppAddress(did) return await makeTx(this.appAddress, this.api, this.provider, 'addFile', [did]) } diff --git a/tests/test.ts b/tests/test.ts index 9cc52ac..5c6fb95 100644 --- a/tests/test.ts +++ b/tests/test.ts @@ -1,33 +1,33 @@ -import test from 'ava' -import sinon from 'sinon' -import { BigNumber, ethers, Wallet } from 'ethers' -import { createEngine } from './sub_provider' -import fs from 'fs' -import nock from 'nock' -import { Blob as nBlob } from 'blob-polyfill' -import axios from 'axios' -import httpAdapter from 'axios/lib/adapters/http' - -import { providerFromEngine } from 'eth-json-rpc-middleware' +import test from 'ava'; +import sinon from 'sinon'; +import { BigNumber, ethers, Wallet } from 'ethers'; +import { createEngine } from './sub_provider'; +import fs from 'fs'; +import nock from 'nock'; +import { Blob as nBlob } from 'blob-polyfill'; +import axios from 'axios'; +import httpAdapter from 'axios/lib/adapters/http'; + +import { providerFromEngine } from 'eth-json-rpc-middleware'; // SDK imports -import { StorageProvider } from '../src/index' -import * as utils from '../src/Utils' -import { parseData } from './utils' -import { CustomError } from '../src/types' -import DID from '../src/contracts/DID' +import { StorageProvider } from '../src/index'; +import * as utils from '../src/Utils'; +import { parseData } from './utils'; +import { CustomError } from '../src/types'; +import DID from '../src/contracts/DID'; // Load contract addresses -const sContracts: any = fs.readFileSync('./tests/contracts.json') -const oContracts = JSON.parse(sContracts) +const sContracts: any = fs.readFileSync('./tests/contracts.json'); +const oContracts = JSON.parse(sContracts); -const gateway = 'http://localhost:9010/api/v1' -const appId = 1 -const appAddress = '445007f942f9Ba718953094Bbe3205B9484cAfd2' -const debug = false +const gateway = 'http://localhost:9010/api/v1'; +const appId = 1; +const appAddress = '445007f942f9Ba718953094Bbe3205B9484cAfd2'; +const debug = false; // To ignore strict http request/response rules -axios.defaults.adapter = httpAdapter -const nockOptions = { 'Access-Control-Allow-Origin': '*' } +axios.defaults.adapter = httpAdapter; +const nockOptions = { 'Access-Control-Allow-Origin': '*' }; /* Not using moxis because of axios instance initialization in SDK @@ -37,39 +37,39 @@ Below to be covered in Integration Tests function sleep(ms) { return new Promise((resolve) => { - setTimeout(resolve, ms) - }) + setTimeout(resolve, ms); + }); } const makeEmail = () => { - const strValues = 'abcdefg12345' - let strEmail = '' - let strTmp + const strValues = 'abcdefg12345'; + let strEmail = ''; + let strTmp; for (let i = 0; i < 10; i++) { - strTmp = strValues.charAt(Math.round(strValues.length * Math.random())) - strEmail = strEmail + strTmp + strTmp = strValues.charAt(Math.round(strValues.length * Math.random())); + strEmail = strEmail + strTmp; } - strTmp = '' - strEmail = strEmail + '@example.com' - return strEmail -} + strTmp = ''; + strEmail = strEmail + '@example.com'; + return strEmail; +}; -let file +let file; // arcanaInstance, // receiverInstance, -const did = '0x4de0e96b0a8886e42a2c35b57df8a9d58a93b5bff655bc37a30e2ab8e29dc066' +const did = '0x4de0e96b0a8886e42a2c35b57df8a9d58a93b5bff655bc37a30e2ab8e29dc066'; function meta_tx_nock(reply_data) { const nockMetaReply = async (uri, body: any) => { - return reply_data ?? { data: 'dummy data', token: 'dummy token' } - } + return reply_data ?? { data: 'dummy data', token: 'dummy token' }; + }; nock(gateway) .defaultReplyHeaders(nockOptions) .post('/meta-tx/') .reply(200, nockMetaReply) .intercept('/meta-tx/', 'OPTIONS') - .reply(200, nockMetaReply, { 'access-control-allow-headers': 'Authorization' }) + .reply(200, nockMetaReply, { 'access-control-allow-headers': 'Authorization' }); } function mock_dkg(reply_data) { @@ -77,7 +77,7 @@ function mock_dkg(reply_data) { .defaultReplyHeaders(nockOptions) .post('/rpc') .times(6) - .reply(200, { jsonrpc: '2.0', result: { ok: true }, id: 10 }) + .reply(200, { jsonrpc: '2.0', result: { ok: true }, id: 10 }); } async function nockSetup() { @@ -90,7 +90,7 @@ async function nockSetup() { Forwarder: oContracts.Forwarder, RPC_URL: 'http://localhost:10002', DID: oContracts.DID, - DKG: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266' + DKG: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266', }) .post('/api/v1/login/') .reply(200, { token: '123456789' }) @@ -105,17 +105,17 @@ async function nockSetup() { .reply(200, { address: oContracts.App }, { 'access-control-allow-headers': 'Authorization' }) .get('/api/v1/get-node-address/') .query(true) - .reply(200, { host: 'http://localhost:3000/', address: storage_node.address }) + .reply(200, { host: 'http://localhost:3000/', address: storage_node.address }); nock('http://localhost:3000') .persist() .defaultReplyHeaders(nockOptions) .patch((p) => p.startsWith('/api/v2/file/')) .reply(200, { - hash: '0xe9e91f1ee4b56c0df2e9f06c2b8c27c6076195a88a7b8537ba8313d80e6f124e' + hash: '0xe9e91f1ee4b56c0df2e9f06c2b8c27c6076195a88a7b8537ba8313d80e6f124e', }) .post((p) => p.startsWith('/api/v2/file/')) - .reply(200, {}) + .reply(200, {}); } function sinonMockObjectSetup() { @@ -124,80 +124,80 @@ function sinonMockObjectSetup() { declaredIp: 'dkgnode1.arcana.network:443', position: '1', pubKx: BigNumber.from('29023421385368379144749466045924017514934229958180852799451398628000593771667'), - pubKy: BigNumber.from('31632158778368581637676511185062566059198308712876704725543144993632262155464') + pubKy: BigNumber.from('31632158778368581637676511185062566059198308712876704725543144993632262155464'), }, { declaredIp: 'dkgnode1.arcana.network:443', position: '2', pubKx: BigNumber.from('105719267757522549686383951453889518570805320580847799971673920448991999863268'), - pubKy: BigNumber.from('12311889399951856112539425386359305279151271210811891657961588078446721210801') + pubKy: BigNumber.from('12311889399951856112539425386359305279151271210811891657961588078446721210801'), }, { declaredIp: 'dkgnode1.arcana.network:443', position: '3', pubKx: BigNumber.from('112513454780213693752054630002769173645973927254986348958538391710171734325064'), - pubKy: BigNumber.from('31826403948237730820406540123018982546704465196666925150128355254483964682271') + pubKy: BigNumber.from('31826403948237730820406540123018982546704465196666925150128355254483964682271'), }, { declaredIp: 'dkgnode1.arcana.network:443', position: '4', pubKx: BigNumber.from('103022124116237959935952092341458720857383888117879935947184525301185593633427'), - pubKy: BigNumber.from('83428276264331813311663241272832111383329363811859329412601611536906464022186') + pubKy: BigNumber.from('83428276264331813311663241272832111383329363811859329412601611536906464022186'), }, { declaredIp: 'dkgnode1.arcana.network:443', position: '5', pubKx: BigNumber.from('72082384183905358797739369765923546941331333550297636524350044306990429216270'), - pubKy: BigNumber.from('661783827736034504670612788123848346528644035307464845748154787461466575102') + pubKy: BigNumber.from('661783827736034504670612788123848346528644035307464845748154787461466575102'), }, { declaredIp: 'dkgnode1.arcana.network:443', position: '6', pubKx: BigNumber.from('30438236858857419456992904193833033911277657186396590512267279659738218054034'), - pubKy: BigNumber.from('27076479865999379327196017777333283283075678191787288284998453473449446886409') - } - ]) - sinon.replace(utils, 'checkTxnStatus', () => Promise.resolve()) - sinon.replace(utils, 'getFile', () => Promise.resolve({ app: '0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9' })) + pubKy: BigNumber.from('27076479865999379327196017777333283283075678191787288284998453473449446886409'), + }, + ]); + sinon.replace(utils, 'checkTxnStatus', () => Promise.resolve()); + sinon.replace(utils, 'getFile', () => Promise.resolve({ app: '0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9' })); } async function mockFile() { // file = MockFile('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.txt', 2 ** 10, 'image/txt'); // file = new File([file], "picsum_img", { type: file.type }); - return new nBlob([await (await fetch('https://picsum.photos/id/872/200/300')).arrayBuffer()]) + return new nBlob([await (await fetch('https://picsum.photos/id/872/200/300')).arrayBuffer()]); } // Wallet Setup -const memonic = 'test test test test test test test test test test test junk' -const path = "m/44'/60'/0'/0/" +const memonic = 'test test test test test test test test test test test junk'; +const path = "m/44'/60'/0'/0/"; -const deployer = ethers.Wallet.fromMnemonic(memonic, path + '0') -const gateway_node = ethers.Wallet.fromMnemonic(memonic, path + '1') -const storage_node = ethers.Wallet.fromMnemonic(memonic, path + '2') -const bridge = ethers.Wallet.fromMnemonic(memonic, path + '3') -const arcanaWallet = ethers.Wallet.fromMnemonic(memonic, path + '4') -const receiverWallet = ethers.Wallet.fromMnemonic(memonic, path + '5') +const deployer = ethers.Wallet.fromMnemonic(memonic, path + '0'); +const gateway_node = ethers.Wallet.fromMnemonic(memonic, path + '1'); +const storage_node = ethers.Wallet.fromMnemonic(memonic, path + '2'); +const bridge = ethers.Wallet.fromMnemonic(memonic, path + '3'); +const arcanaWallet = ethers.Wallet.fromMnemonic(memonic, path + '4'); +const receiverWallet = ethers.Wallet.fromMnemonic(memonic, path + '5'); // shared fixture -let fixture +let fixture; // Mock server & stub setup test.serial.before(async (t) => { // Mock gateway response(s) setup - nockSetup() + nockSetup(); // Mock basic Storage utils - sinonMockObjectSetup() + sinonMockObjectSetup(); // File prep - file = await mockFile() - file.name = 'test_file' -}) + file = await mockFile(); + file.name = 'test_file'; +}); // TODO: get request handler in arrays async function createStorageInstance(wallet: Wallet, middleware?) { - const engine = createEngine(wallet.address) + const engine = createEngine(wallet.address); if (middleware) { - engine.push(middleware) + engine.push(middleware); } const instance = await StorageProvider.init({ @@ -206,14 +206,14 @@ async function createStorageInstance(wallet: Wallet, middleware?) { gateway: gateway + '/', debug, chainId: 100, - provider: providerFromEngine(engine) - }) + provider: providerFromEngine(engine), + }); - return Promise.resolve(instance) + return Promise.resolve(instance); } test.serial('Upload file', async (t) => { - meta_tx_nock(undefined) + meta_tx_nock(undefined); const arcanaInstance = await createStorageInstance(arcanaWallet, (req, res, next, end) => { if (req.method === 'eth_getTransactionByHash') { @@ -233,8 +233,8 @@ test.serial('Upload file', async (t) => { transactionIndex: '0x1', type: '0x0', v: '0x1c', - value: '0x6113a84987be800' - } + value: '0x6113a84987be800', + }; } else if (req.method === 'eth_getTransactionReceipt') { res.result = { transactionHash: '0xe9e91f1ee4b56c0df2e9f06c2b8c27c6076195a88a7b8537ba8313d80e6f124e', @@ -251,19 +251,19 @@ test.serial('Upload file', async (t) => { status: '0x1', to: '0xdf190dc7190dfba737d7777a163445b7fff16133', transactionIndex: '0x1', - type: '0x0' - } + type: '0x0', + }; } - end() - }) + end(); + }); - const upload = await arcanaInstance.getUploader() - await t.notThrowsAsync(upload.upload(file)) -}) + const upload = await arcanaInstance.getUploader(); + await t.notThrowsAsync(upload.upload(file)); +}); test.skip('Download file', async (t) => { // unable to fake key shares responses -}) +}); test.serial.skip('Metadata URL', async (t) => { // Skipped because axios need additional transformation for nodejs env @@ -272,42 +272,42 @@ test.serial.skip('Metadata URL', async (t) => { .defaultReplyHeaders(nockOptions) .post('/api/v1/nft') .reply(200, (req) => { - Promise.resolve({ data: { request: { responseURL: 'dummy.image.url' } } }) - }) + Promise.resolve({ data: { request: { responseURL: 'dummy.image.url' } } }); + }); nock(gateway) .post('/metadata/') - .reply(201, Promise.resolve({ request: { responseURL: 'dummy.metadata.url' } })) - const freader = await (await fetch('https://picsum.photos/id/872/200/300')).arrayBuffer() - const arcanaInstance = await createStorageInstance(arcanaWallet) - const metadataURL = await arcanaInstance.makeMetadataURL('test', 'test description', did, file) + .reply(201, Promise.resolve({ request: { responseURL: 'dummy.metadata.url' } })); + const freader = await (await fetch('https://picsum.photos/id/872/200/300')).arrayBuffer(); + const arcanaInstance = await createStorageInstance(arcanaWallet); + const metadataURL = await arcanaInstance.makeMetadataURL('test', 'test description', did, file); - t.is(metadataURL, 'dummy.metadata.url'.concat('/', did)) -}) + t.is(metadataURL, 'dummy.metadata.url'.concat('/', did)); +}); test.serial('Share file', async (t) => { - t.plan(4) - meta_tx_nock(undefined) + t.plan(4); + meta_tx_nock(undefined); const middleware = (req, res, next, end) => { const data = parseData( { value: ethers.utils.parseEther('0'), - data: req.params[0].data + data: req.params[0].data, }, - DID.abi - ) + DID.abi, + ); switch (data.name) { case 'getRuleSet': - res.result = ethers.constants.HashZero + res.result = ethers.constants.HashZero; } - end() - } + end(); + }; - const arcanaInstance = await createStorageInstance(arcanaWallet, middleware) + const arcanaInstance = await createStorageInstance(arcanaWallet, middleware); - const access = await arcanaInstance.getAccess() + const access = await arcanaInstance.getAccess(); // Now check whether it showing in receipt user list nock(gateway) @@ -320,31 +320,31 @@ test.serial('Share file', async (t) => { .get('/get-hash-data/?hash=0x0000000000000000000000000000000000000000000000000000000000000000') .reply(200, null) .post('/update-hash/') - .reply(200, {}) + .reply(200, {}); - const tx = await access.share(did, [receiverWallet.address], [150]) - t.truthy(tx) + const tx = await access.share(did, [receiverWallet.address], [150]); + t.truthy(tx); - const receiverInstance = await createStorageInstance(receiverWallet) + const receiverInstance = await createStorageInstance(receiverWallet); - const files = await receiverInstance.sharedFiles() - t.is(files.length, 1) - t.is(files[0].did, did.substring(2)) - t.is(files[0].size, file.size) -}) + const files = await receiverInstance.sharedFiles(); + t.is(files.length, 1); + t.is(files[0].did, did.substring(2)); + t.is(files[0].size, file.size); +}); test.serial('Fail revoke transaction on unauthorized files', async (t) => { - t.plan(3) - const expected_errorCode = 'You dont have access to perform this operation' + t.plan(3); + const expected_errorCode = 'You dont have access to perform this operation'; nock(gateway) .defaultReplyHeaders(nockOptions) .post('/update-hash/') .reply(200, { - err: expected_errorCode + err: expected_errorCode, }) .get('/get-hash-data/') .query(true) - .reply(200, null) + .reply(200, null); const middleware = (req, res, next, end) => { switch (req.method) { @@ -355,87 +355,87 @@ test.serial('Fail revoke transaction on unauthorized files', async (t) => { ethers.BigNumber.from('120000'), true, ethers.utils.randomBytes(120), - ethers.utils.id('random_address').substring(0, 42) - ] - ) - break + ethers.utils.id('random_address').substring(0, 42), + ], + ); + break; } } - end() - } - const receiverInstance = await createStorageInstance(receiverWallet, middleware) - const err = (await t.throwsAsync(receiverInstance.files.revoke(did, arcanaWallet.address))) as CustomError - t.true(err.message.endsWith(expected_errorCode)) - t.assert(err.code.startsWith('TRANSACTION')) -}) + end(); + }; + const receiverInstance = await createStorageInstance(receiverWallet, middleware); + const err = (await t.throwsAsync(receiverInstance.files.revoke(did, arcanaWallet.address))) as CustomError; + t.true(err.message.endsWith(expected_errorCode)); + t.assert(err.code.startsWith('TRANSACTION')); +}); test.serial('Get consumed and total upload limit', async (t) => { const middleware = (req, res, next, end) => { const data = parseData({ value: ethers.utils.parseEther('0'), - data: req.params[0].data - }) + data: req.params[0].data, + }); switch (data.name) { case 'limit': - res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [100000000, 100000000]) - break + res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [100000000, 100000000]); + break; case 'consumption': - res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [file.size, 0]) - break + res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [file.size, 0]); + break; case 'defaultLimit': - res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [100000000, 100000000]) - break + res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [100000000, 100000000]); + break; } - end() - } + end(); + }; - const arcanaInstance = await createStorageInstance(arcanaWallet, middleware) - const Access = await arcanaInstance.getAccess() - const [consumed, total] = await Access.getUploadLimit() - t.is(consumed, file.size) - t.is(total, 100000000) -}) + const arcanaInstance = await createStorageInstance(arcanaWallet, middleware); + const Access = await arcanaInstance.getAccess(); + const [consumed, total] = await Access.getUploadLimit(); + t.is(consumed, file.size); + t.is(total, 100000000); +}); test.serial('Get consumed and total download limit', async (t) => { const middleware = (req, res, next, end) => { const data = parseData({ value: ethers.utils.parseEther('0'), - data: req.params[0].data - }) + data: req.params[0].data, + }); switch (data.name) { case 'limit': - res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [100000000, 100000000]) - break + res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [100000000, 100000000]); + break; case 'consumption': - res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [0, file.size]) - break + res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [0, file.size]); + break; case 'defaultLimit': - res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [100000000, 100000000]) - break + res.result = ethers.utils.defaultAbiCoder.encode(['uint', 'uint'], [100000000, 100000000]); + break; } - end() - } + end(); + }; - const arcanaInstance = await createStorageInstance(arcanaWallet, middleware) - const Access = await arcanaInstance.getAccess() - const [consumed, total] = await Access.getDownloadLimit() - t.is(consumed, file.size) - t.is(total, 100000000) -}) + const arcanaInstance = await createStorageInstance(arcanaWallet, middleware); + const Access = await arcanaInstance.getAccess(); + const [consumed, total] = await Access.getDownloadLimit(); + t.is(consumed, file.size); + t.is(total, 100000000); +}); test.serial('Revoke', async (t) => { - meta_tx_nock(null) + meta_tx_nock(null); let middleware = (req, res, next, end) => { if (req.method == 'eth_call') { - res.result = ethers.utils.defaultAbiCoder.encode(['address[]'], [[receiverWallet.address]]) + res.result = ethers.utils.defaultAbiCoder.encode(['address[]'], [[receiverWallet.address]]); } - end() - } + end(); + }; - let arcanaInstance = await createStorageInstance(arcanaWallet, middleware) + let arcanaInstance = await createStorageInstance(arcanaWallet, middleware); - let access = await arcanaInstance.getAccess() + let access = await arcanaInstance.getAccess(); nock(gateway) .defaultReplyHeaders(nockOptions) .get('/shared-users/?did=' + did) @@ -444,34 +444,34 @@ test.serial('Revoke', async (t) => { .reply(200, {}) .get('/get-hash-data/') .query(true) - .reply(200, null) + .reply(200, null); - const beforeRevokeUsers = await access.getSharedUsers(did) - const tx = await access.revoke(did, receiverWallet.address) - t.truthy(tx) + const beforeRevokeUsers = await access.getSharedUsers(did); + const tx = await access.revoke(did, receiverWallet.address); + t.truthy(tx); middleware = (req, res, next, end) => { if (req.method == 'eth_call') { switch (true) { case req.params[0].data.startsWith('0x6184533f'): - res.result = ethers.utils.defaultAbiCoder.encode(['address[]'], [[]]) - break + res.result = ethers.utils.defaultAbiCoder.encode(['address[]'], [[]]); + break; } } - end() + end(); }; - (arcanaInstance = await createStorageInstance(arcanaWallet, middleware)), (access = await arcanaInstance.getAccess()) + (arcanaInstance = await createStorageInstance(arcanaWallet, middleware)), (access = await arcanaInstance.getAccess()); nock(gateway) .defaultReplyHeaders(nockOptions) .get('/shared-users/?did=' + did) - .reply(200, [], { 'access-control-allow-headers': 'Authorization' }) - const afterRevokeUsers = await access.getSharedUsers(did) + .reply(200, [], { 'access-control-allow-headers': 'Authorization' }); + const afterRevokeUsers = await access.getSharedUsers(did); - t.is(beforeRevokeUsers.includes(receiverWallet.address), true) - t.is(afterRevokeUsers.includes(receiverWallet.address), false) - t.is(beforeRevokeUsers.length - afterRevokeUsers.length, 1) + t.is(beforeRevokeUsers.includes(receiverWallet.address), true); + t.is(afterRevokeUsers.includes(receiverWallet.address), false); + t.is(beforeRevokeUsers.length - afterRevokeUsers.length, 1); await nock(gateway) .defaultReplyHeaders(nockOptions) @@ -479,16 +479,16 @@ test.serial('Revoke', async (t) => { .query(true) .reply(200, [], { 'access-control-allow-headers': 'Authorization' }) .get('/files/shared/total/') - .reply(200, { data: 0 }) + .reply(200, { data: 0 }); - const receiverInstance = await createStorageInstance(receiverWallet) + const receiverInstance = await createStorageInstance(receiverWallet); - const files = await receiverInstance.sharedFiles() - t.is(files.length, 0) -}) + const files = await receiverInstance.sharedFiles(); + t.is(files.length, 0); +}); test.serial('Delete File', async (t) => { - meta_tx_nock(null) + meta_tx_nock(null); const scope = nock(gateway) .defaultReplyHeaders(nockOptions) @@ -496,18 +496,18 @@ test.serial('Delete File', async (t) => { .query(true) .reply(200, [{ did: did.substring(2) }], { 'access-control-allow-headers': 'Authorization' }) .get('/files/total/') - .reply(200, { data: 1 }) + .reply(200, { data: 1 }); - const arcanaInstance = await createStorageInstance(arcanaWallet) + const arcanaInstance = await createStorageInstance(arcanaWallet); - const access = await arcanaInstance.getAccess() + const access = await arcanaInstance.getAccess(); - let files = await arcanaInstance.myFiles() + let files = await arcanaInstance.myFiles(); - t.is(files.length, 1) - t.is(files[0].did, did.substring(2)) + t.is(files.length, 1); + t.is(files[0].did, did.substring(2)); - const tx = await access.deleteFile(did) + const tx = await access.deleteFile(did); nock(gateway) .defaultReplyHeaders(nockOptions) @@ -515,107 +515,106 @@ test.serial('Delete File', async (t) => { .query(true) .reply(200, [], { 'access-control-allow-headers': 'Authorization' }) .get('/files/total/') - .reply(200, { data: 0 }) + .reply(200, { data: 0 }); - files = await arcanaInstance.myFiles() + files = await arcanaInstance.myFiles(); - t.is(files.length, 0) - t.truthy(tx) -}) + t.is(files.length, 0); + t.truthy(tx); +}); test.serial('Grant app permission', async (t) => { - meta_tx_nock(null) + meta_tx_nock(null); const middleware = (req, res, next, end) => { const data = parseData({ value: ethers.utils.parseEther('0'), - data: req.params[0].data - }) + data: req.params[0].data, + }); switch (data.name) { case 'appLevelControl': - res.result = ethers.utils.defaultAbiCoder.encode(['uint8'], [1]) - break + res.result = ethers.utils.defaultAbiCoder.encode(['uint8'], [1]); + break; case 'userAppPermission': - res.result = ethers.utils.defaultAbiCoder.encode(['uint8'], [0]) + res.result = ethers.utils.defaultAbiCoder.encode(['uint8'], [0]); } - end() - } + end(); + }; - const arcanaInstance = await createStorageInstance(arcanaWallet, middleware) - await t.notThrowsAsync(arcanaInstance.grantAppPermission()) -}) + const arcanaInstance = await createStorageInstance(arcanaWallet, middleware); + await t.notThrowsAsync(arcanaInstance.grantAppPermission()); +}); -test.serial("Add file to app", async (t) => { - t.plan(7) - meta_tx_nock(null) +test.serial('Add file to app', async (t) => { + t.plan(7); + meta_tx_nock(null); let scope = nock(gateway) .defaultReplyHeaders(nockOptions) .get('/list-files/') .query(true) .reply(200, [{ did: did.substring(2) }], { 'access-control-allow-headers': 'Authorization' }) .get('/files/total/') - .reply(200, { data: 1 }) + .reply(200, { data: 1 }); + + const arcanaInstance = await createStorageInstance(arcanaWallet); + + let files: any = await arcanaInstance.myFiles(); - const arcanaInstance = await createStorageInstance(arcanaWallet) - - let files:any = await arcanaInstance.myFiles() + t.true(scope.isDone()); + t.is(files.length, 1); + t.is(files[0].did, did.substring(2)); - t.true(scope.isDone()) - t.is(files.length, 1) - t.is(files[0].did, did.substring(2)) + const did2 = '0x4de0e96b0a8886e42a2c35b57df8a9d58a93b5bff655bc37a30e2ab8e29dc066'; - const did2 = "0x4de0e96b0a8886e42a2c35b57df8a9d58a93b5bff655bc37a30e2ab8e29dc066" + await t.notThrowsAsync(arcanaInstance.files.addFile(did2)); - const access = await arcanaInstance.getAccess(); - await t.notThrowsAsync( access.addFile(did2)); - - scope = nock(gateway) + scope = nock(gateway) .defaultReplyHeaders(nockOptions) .get('/list-files/') .query(true) - .reply(200, [{ did: did.substring(2) }, {did: did2.substring(2)}], { 'access-control-allow-headers': 'Authorization' }) + .reply(200, [{ did: did.substring(2) }, { did: did2.substring(2) }], { + 'access-control-allow-headers': 'Authorization', + }) .get('/files/total/') - .reply(200, { data: 2 }) - - files = await arcanaInstance.myFiles() - t.true(scope.isDone()) - t.is(files.length, 2) - t.is(files[1].did, did2.substring(2)) - -}) - -test.serial("Remove file from app", async (t) => { - t.plan(6) - meta_tx_nock(null) + .reply(200, { data: 2 }); + + files = await arcanaInstance.myFiles(); + t.true(scope.isDone()); + t.is(files.length, 2); + t.is(files[1].did, did2.substring(2)); +}); + +test.serial('Remove file from app', async (t) => { + t.plan(6); + meta_tx_nock(null); let scope = nock(gateway) .defaultReplyHeaders(nockOptions) .get('/list-files/') .query(true) .reply(200, [{ did: did.substring(2) }], { 'access-control-allow-headers': 'Authorization' }) .get('/files/total/') - .reply(200, { data: 1 }) + .reply(200, { data: 1 }); + + const arcanaInstance = await createStorageInstance(arcanaWallet); - const arcanaInstance = await createStorageInstance(arcanaWallet) - - let files:any = await arcanaInstance.myFiles() + let files: any = await arcanaInstance.myFiles(); - t.true(scope.isDone()) - t.is(files.length, 1) - t.is(files[0].did, did.substring(2)) + t.true(scope.isDone()); + t.is(files.length, 1); + t.is(files[0].did, did.substring(2)); - const access = await arcanaInstance.getAccess(); - await t.notThrowsAsync( access.removeFileFromApp(did)); - - scope = nock(gateway) + // const access = await arcanaInstance.getAccess(); + await t.notThrowsAsync(arcanaInstance.files.removeFileFromApp(did)); + + scope = nock(gateway) .defaultReplyHeaders(nockOptions) .get('/list-files/') .query(true) .reply(200, [], { 'access-control-allow-headers': 'Authorization' }) .get('/files/total/') - .reply(200, { data: 0 }) - - files = await arcanaInstance.myFiles() - t.true(scope.isDone()) - t.is(files.length, 0) + .reply(200, { data: 0 }); -}) \ No newline at end of file + files = await arcanaInstance.myFiles(); + t.true(scope.isDone()); + t.is(files.length, 0); +}); diff --git a/tests/utils.ts b/tests/utils.ts index 8bfa87d..e0ec7da 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -1,7 +1,7 @@ -import { ethers } from 'ethers' -import arcana from '../src/contracts/Arcana' +import { ethers } from 'ethers'; +import arcana from '../src/contracts/Arcana'; export const parseData = (data: any, abi?: any[]) => { - const iface = new ethers.utils.Interface(abi ?? arcana.abi) - const pt = iface.parseTransaction(data) - return { name: pt.functionFragment.name, args: pt.args } -} + const iface = new ethers.utils.Interface(abi ?? arcana.abi); + const pt = iface.parseTransaction(data); + return { name: pt.functionFragment.name, args: pt.args }; +}; From e2dc2e13b404c11f0d33a058950f5376f5fe91b2 Mon Sep 17 00:00:00 2001 From: shalz Date: Sat, 29 Oct 2022 18:26:50 +0530 Subject: [PATCH 4/6] Updated AddFile, RemoveFile and cleaned up Grant Permission API IsPermmission Required API Add/Remove File API docs cleaned up @steel-feel pls take a look if this works. --- usage.md | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/usage.md b/usage.md index cbcc384..03c0919 100644 --- a/usage.md +++ b/usage.md @@ -269,34 +269,45 @@ dAppStorageProvider.onAccountChange = (accounts) => { } ``` +## Delegate Data Access Permissions -## App permissions +A delegate may perform data access operations as per the access rights granted to them by the data owner. For example, a moderator reviewing a stream of tweets for a decentralized dApp may be granted permissions to delete objectionable tweets, or simply flag them. -App might require to pass additional permissions for app delegates. +The following APIs support data access permission delegation and empowers the dApp users to own their data. The dApps can enable users to disallow the dApp from listing files that are owned by the user by calling removeFile. The dApp can also enable users to add files that are owned by the user and uploaded via a different dApp. -### Grant App permissions +**Note** +In the current release, a dApp user can delegate data access permissions to a dApp developer, if they choose to. In the future, we may support third-party services that take on the delegation role. + +### Grant Delegator Permission to dApp + +This API can be used by a dApp to seek user's permission to get the role of a delegatee with data access control on behalf of the data owner. The user can choose what kind of data access actions can be performed by the delegatee. For example, a delegatee may simply be allowed to delete the data but not download it. ```js await storage.grantAppPermission() ``` -### Check whether user needs to grant permissions to the app. -returns `true` if required. +### Check if dAPP requires Delegator Permission + +The dApp can use this API to check if it needs to seek delegator permissions from the dApp user. It returns `true` if dApp requires permission. ```js const isPermissionRequired = await storage.checkPermission() -> boolean ``` -### Add existing file to App -Add an already uploaded file to selected app. +### Add File to dApp + +A dApp user can upload files to the Arcana Store. User can also grant delegator permission to the dApp and allow the dApp to perform the granted actions on a set of files owned by the user. This API allows the user to add an already uploaded file to the list of files that the dApp can access as per the granted permissions. ```js await storage.files.addFile() ``` ### Remove file from App -Removes file from selected app. Does **NOT** delete from arcana storage, use `delete` for that case. + +A dApp user can grant delegator permission to the dApp and add a list of data files that the dApp can access as per the granted permissions. Later, the dApp user can remove one or more such files from the list of files that the dApp has been granted delegator permission. + +The removal of file(s) from the list of files a dApp is granted access to does **NOT** delete it from the Arcana storage. To delete the file, use `delete` API. ```js await storage.files.removeFileFromApp() -``` \ No newline at end of file +``` From 57527beb611f06113572f29407d55f7c6ea9c3bd Mon Sep 17 00:00:00 2001 From: shalz Date: Sat, 29 Oct 2022 18:28:06 +0530 Subject: [PATCH 5/6] Add and Remove File documentation in Usage Guide Incorporated @steel-feel suggestions and removed delegator stuff. Add/Remove is not part of delegator but for dApp context change. Created a separate section after Delegate (Grant, ispermission) section. --- .DS_Store | Bin 8196 -> 0 bytes usage.md | 34 ++++++++++++++++++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index c692c05b1e6c279062d701dd320f3a660a976fae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHMJ!n%=6h1diG57;jN$ny&akMRzLKi`vB?=X>NQb(JX?{owNndD_`h%1#4x)n= z#5y_Xpo6>U(m`+yj*5sNxG0E=gMxndC&{^aFCm?j_Fj1Ryxw!pJKy=v?R`D3L?otb zxe1~XB1+(ra%c~xh#~dz3XPaq8HE(^r&3{JE*q39AxpR57;p?Y1{?#90ms1q!T{dc zawNLE_qEo|jseHOj$}Zd4<0TleIx5ys-pvwY5^eQSXKk~L-GdFGtxJ*t|c@WLc2n0 zS7BNVq200W89U#|x|Z6VglRs6i7ZTqB2;ud-&5u!d@bGV7;p@P8IZkuoW`j@i&VAx z_r{+!t}6$rRHhVUu$K5aGdsKQ#_LnIKG#!UDYm~2h=bqZO1qkIsYo@-SkY;CK5W4; z1h*f(IDA22=y*;!Xd~%v#qsFlP2pLk70OaTIm+93)OjQ$F$<38-j^rIizixmEUPx2 z?p8J)b&4evl2yvnGA*D&Hlp>-A7ChC!!ezFJGOh{ioz6O*GARL&ce0ckadfLokj3u z@0a68-*X+uaenIR(j|?f<;$xSrMKdaZZ_iv;AF+F%;Y;0SLt}%TSvyf-pQMNnnP9L z%i~7rt+EQs2xw2sjTj5R6@2E&j%VGp7pZjx!n9TJotP~<{4$!_uJO;xUn}I&AKvw9y{8? zichLe(*GJ&Ix9mo`Ow5SS|0z*=Wj*xyT|-_{Add+zVHyhjcvxjfX-(`_W!55fB)ZR zS-W%`1C9aCfQYB&Qqx#5y*1aVd)aGyxYD@fkal$~l?x`-jzg+;9J2KfL)?2Xl|H_a ZbuF=i@ee)(xc9&HSFn5ktBttBroYKY#s~la diff --git a/usage.md b/usage.md index 03c0919..9f619ed 100644 --- a/usage.md +++ b/usage.md @@ -27,9 +27,9 @@ Refer to the [Arcana Storage SDK Quick Start Guide](https://docs.beta.arcana.net ## Usage Flow 1. Install Storage SDK -2. Import `StorageProvider` from the Storage SDK package in the dApp. Call `init` method of `StorageProvider` and specify the Web3 wallet `provider` and the `appId` as input parameters. **Note:** Get the provider via the Auth SDK or third-party supported wallet. You can copy the appId from the [Arcana Developer Dashboard](https://docs.beta.arcana.network/docs/config_dapp) after registering your dApp +2. Import `StorageProvider` from the Storage SDK package in the dApp. Call the `init` method of `StorageProvider` and specify the Web3 wallet `provider` and the `appId` as input parameters. **Note:** Get the provider via the Auth SDK or third-party supported wallet. You can copy the appId from the [Arcana Developer Dashboard](https://docs.beta.arcana.network/docs/config_dapp) after registering your dApp 3. Use `StorageProvider` to: - - `upload` and push file data into the Arcana Store. **Note:** Save file DID that is returned after file upload operation is successful. + - `upload` and push file data into the Arcana Store. **Note:** Save file DID that is returned after the successful file upload. - `download` a file from the Arcana Store using DID as input. 4. Use `StorageProvider.files` to: - `delete` a file by specifying its DID. @@ -65,7 +65,7 @@ This *Singleton* usage is recommended as a best practice. The Storage SDK accepts `Blob`s as files. The `file` object passed must be an instance of a `Blob` or a descendant (`File`, etc.). You cannot upload a file by providing its URL. -As of now, it supports uploading _private_ and _public_ files. They are identifiable by looking at the first byte of the DID. In hexadecimal format, 01 indicates it's a public file, and 02 indicates it's a private file. +As of now, it supports uploading _private_ and _public_ files. They are identifiable by looking at the first byte of the DID. In hexadecimal format, 01 indicates a public file, and 02 indicates a private file. ### Private Files @@ -117,7 +117,7 @@ await dAppStorageProvider.download( ### Share a File ```ts -// did: DID of file to be shared +// did: DID of the file to be shared // address: recipient user's address // validity (optional): For how long will the user be able to download the file, e.g. [400] would mean 400 seconds await dAppStorageProvider.files.share([did], [address]); @@ -127,7 +127,7 @@ await dAppStorageProvider.files.share([did], [address]); ```ts // did: DID of file from which access is removed -// address: Address of the user for whom the access must be revoked +// address: The address of the user for whom the access must be revoked await dAppStorageProvider.files.revoke(did, address); ``` @@ -156,7 +156,7 @@ let [consumed, total] = await dAppStorageProvider.files.getUploadLimit(); ### Get Download Limit ```ts -//Get consumed and total bandwidth of the current user +//Get consumed and the total bandwidth of the current user let [consumed, total] = await dAppStorageProvider.files.getDownloadLimit(); ``` @@ -183,7 +183,7 @@ let files = await dAppStorageProvider.files.list(AccessTypeEnum.MY_FILES); ```ts //The file DID is returned at the time of file upload and uniquely identifies the file in Arcana Store. -//Note: No appID is required during initialization of the Storage SDK in order to +//Note: No appID is required during the initialization of the Storage SDK to //download a file using the file DID. // Pass the provider during initialization of the Storage SDK, if required. @@ -213,7 +213,7 @@ let metadata = await dAppStorageProvider.makeMetadataURL( title, description, did, // The DID of the private NFT file hosted in the Arcana Store - file, // The 'preview image' file corresponding to the private NFT, not the actual private NFT data file + file, // The 'preview image file corresponding to the private NFT, not the actual private NFT data file ); console.log(metadata); // https://test-storage.arcana.network:9000/api/v1/metadata/0x129d1438ff3bf014e9b9094b3a5d410f691c208ed5305b0844307b761c0e295e @@ -221,7 +221,7 @@ console.log(metadata); ### Link Minted NFT with DID -Once you have minted the NFT, to make it private and control access to it and manage ownership, you need to link it with the DID. +Once you have minted the NFT, you need to link it with the DID to make it private, control access, and manage ownership. ```ts let chainId = 80001,tokenId = 3, nftContract = "0xE80FCAD702b72777f5036eF1a76086FD3f882E29" @@ -273,14 +273,14 @@ dAppStorageProvider.onAccountChange = (accounts) => { A delegate may perform data access operations as per the access rights granted to them by the data owner. For example, a moderator reviewing a stream of tweets for a decentralized dApp may be granted permissions to delete objectionable tweets, or simply flag them. -The following APIs support data access permission delegation and empowers the dApp users to own their data. The dApps can enable users to disallow the dApp from listing files that are owned by the user by calling removeFile. The dApp can also enable users to add files that are owned by the user and uploaded via a different dApp. +The following APIs support data access permission delegation. **Note** In the current release, a dApp user can delegate data access permissions to a dApp developer, if they choose to. In the future, we may support third-party services that take on the delegation role. ### Grant Delegator Permission to dApp -This API can be used by a dApp to seek user's permission to get the role of a delegatee with data access control on behalf of the data owner. The user can choose what kind of data access actions can be performed by the delegatee. For example, a delegatee may simply be allowed to delete the data but not download it. +This API can be used by a dApp to seek the user's permission to get the role of a delegate with data access control on behalf of the data owner. ```js await storage.grantAppPermission() @@ -294,19 +294,21 @@ The dApp can use this API to check if it needs to seek delegator permissions fro const isPermissionRequired = await storage.checkPermission() -> boolean ``` +## Inject/Eject Data + +The inject/eject APIs empower the dApp users to own their data. A dApp user can choose to upload data to the Arcana Store via a dApp that is integrated with the Arcana Storage SDK. Later, they can use another dApp integrated with the Storage SDK and access the same data uploaded using the previous dApp into the Arcana Store. This is done by injecting or adding the data file into the new dApp context. Similarly, dApp users can eject or remove a data file that was uploaded using a dApp. If a data file is removed from the context of a dApp, the same user cannot access the file from that dApp. The removal from a dApp context does not delete the file from the Arcana Store. + ### Add File to dApp -A dApp user can upload files to the Arcana Store. User can also grant delegator permission to the dApp and allow the dApp to perform the granted actions on a set of files owned by the user. This API allows the user to add an already uploaded file to the list of files that the dApp can access as per the granted permissions. +A dApp user can upload files to the Arcana Store. To access the same file from a different dApp, the file must be injected in the new dApp context. Use this API to add an already uploaded file to a new dApp context. ```js await storage.files.addFile() ``` -### Remove file from App - -A dApp user can grant delegator permission to the dApp and add a list of data files that the dApp can access as per the granted permissions. Later, the dApp user can remove one or more such files from the list of files that the dApp has been granted delegator permission. +### Remove File from dApp -The removal of file(s) from the list of files a dApp is granted access to does **NOT** delete it from the Arcana storage. To delete the file, use `delete` API. +Whenever a dApp user uploads a file using a dApp or uses the `addFile` API for an already uploaded file, the data file gets added to the dApp context. This enables a user to access a file uploaded using any dApp from more than one dApp context. To stop file access, the file must be ejected from the dApp's context. Use `removeFile` API to remove it from a dApp's context. The removal of the file(s) does **NOT** delete the file(s) from the Arcana storage. To delete the file, use the `delete` API. ```js await storage.files.removeFileFromApp() From 5bdb9dffd76e7ea7f844bf98e7e854e22398fc92 Mon Sep 17 00:00:00 2001 From: shaloo <5890484+shaloo@users.noreply.github.com> Date: Mon, 31 Oct 2022 12:23:13 +0530 Subject: [PATCH 6/6] Refs: #AR-4809 app ID -> app Address update --- usage.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/usage.md b/usage.md index 9f619ed..b54c707 100644 --- a/usage.md +++ b/usage.md @@ -27,7 +27,7 @@ Refer to the [Arcana Storage SDK Quick Start Guide](https://docs.beta.arcana.net ## Usage Flow 1. Install Storage SDK -2. Import `StorageProvider` from the Storage SDK package in the dApp. Call the `init` method of `StorageProvider` and specify the Web3 wallet `provider` and the `appId` as input parameters. **Note:** Get the provider via the Auth SDK or third-party supported wallet. You can copy the appId from the [Arcana Developer Dashboard](https://docs.beta.arcana.network/docs/config_dapp) after registering your dApp +2. Import `StorageProvider` from the Storage SDK package in the dApp. Call the `init` method of `StorageProvider` and specify the Web3 wallet `provider` and the `appAddress` as input parameters. **Note:** Get the provider via the Auth SDK or third-party supported wallet. You can copy the **App Address** from the [Arcana Developer Dashboard](https://docs.beta.arcana.network/docs/config_dapp) after registering your dApp. In the earlier releases, **App Address** was referred to as **App ID** in the dashboard. 3. Use `StorageProvider` to: - `upload` and push file data into the Arcana Store. **Note:** Save file DID that is returned after the successful file upload. - `download` a file from the Arcana Store using DID as input. @@ -183,7 +183,8 @@ let files = await dAppStorageProvider.files.list(AccessTypeEnum.MY_FILES); ```ts //The file DID is returned at the time of file upload and uniquely identifies the file in Arcana Store. -//Note: No appID is required during the initialization of the Storage SDK to +//Note: No **App Address** needs to be specified during the initialization of the Storage SDK +//if a dApp only requires to //download a file using the file DID. // Pass the provider during initialization of the Storage SDK, if required.