diff --git a/.github/workflows/tests-rc.yaml b/.github/workflows/tests-rc.yaml index 47cb462d..5393d352 100644 --- a/.github/workflows/tests-rc.yaml +++ b/.github/workflows/tests-rc.yaml @@ -29,23 +29,7 @@ jobs: run: npm ci - name: Install global dependencies - run: npm install --global bee-dev @fairdatasociety/fdp-play @ethersphere/swarm-cli npxie - - - name: Clone fdp-play - run: git clone https://github.com/fairDataSociety/fdp-play - - - name: Install fdp-play dependencies - run: | - cd fdp-play && npm install - cd orchestrator && npm install - - - name: Build fdp-play images - run: | - cd fdp-play/orchestrator - npm run build:env -- --build-base-bee --bee-repository=ethersphere/bee.git - - - name: Clean up fdp-play - run: rm -rf fdp-play/test + run: npm install --global bee-dev @ethersphere/bee-factory @ethersphere/swarm-cli npxie - name: Run bee-dev run: bee-dev --port 16337 & @@ -53,16 +37,8 @@ jobs: - name: Run bee-dev with --no-swap run: bee-dev --port 16338 --no-swap & - - name: Start fdp-play environment - run: fdp-play start --detach --blockchain-image ethereum/client-go:release-1.13 --bee-version HEAD-commit - - - name: Deposit to chequebook - run: | - swarm-cli cheque deposit 10 - swarm-cli cheque deposit 10 --bee-api-url http://localhost:11633 - swarm-cli cheque deposit 10 --bee-api-url http://localhost:21633 - swarm-cli cheque deposit 10 --bee-api-url http://localhost:31633 - swarm-cli cheque deposit 10 --bee-api-url http://localhost:41633 + - name: Start bee-factory + run: bee-factory start --tag master - name: Print swarm-cli status continue-on-error: true diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 24ca56ae..e7e9895b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -29,7 +29,7 @@ jobs: run: npm ci - name: Install global dependencies - run: npm install --global bee-dev @fairdatasociety/fdp-play @ethersphere/swarm-cli npxie + run: npm install --global bee-dev @ethersphere/bee-factory @ethersphere/swarm-cli npxie - name: Run bee-dev run: bee-dev --port 16337 & @@ -37,16 +37,8 @@ jobs: - name: Run bee-dev with --no-swap run: bee-dev --port 16338 --no-swap & - - name: Start fdp-play environment - run: fdp-play start --detach --fresh --bee-version d0aa8b9-commit - - - name: Deposit to chequebook - run: | - swarm-cli cheque deposit 10 - swarm-cli cheque deposit 10 --bee-api-url http://localhost:11633 - swarm-cli cheque deposit 10 --bee-api-url http://localhost:21633 - swarm-cli cheque deposit 10 --bee-api-url http://localhost:31633 - swarm-cli cheque deposit 10 --bee-api-url http://localhost:41633 + - name: Start bee-factory + run: bee-factory start --tag v2.8.0 - name: Print swarm-cli status continue-on-error: true diff --git a/src/command/access/access-command.ts b/src/command/access/access-command.ts new file mode 100644 index 00000000..51f1149e --- /dev/null +++ b/src/command/access/access-command.ts @@ -0,0 +1,3 @@ +import { RootCommand } from '../root-command' + +export class AccessCommand extends RootCommand {} diff --git a/src/command/access/index.ts b/src/command/access/index.ts new file mode 100644 index 00000000..b02d7d82 --- /dev/null +++ b/src/command/access/index.ts @@ -0,0 +1,10 @@ +import { GroupCommand } from 'furious-commander' +import { Init } from './init' + +export class Access implements GroupCommand { + public readonly name = 'access' + + public readonly description = 'Share access to your uploaded files/folders' + + public subCommandClasses = [Init] +} diff --git a/src/command/access/init.ts b/src/command/access/init.ts new file mode 100644 index 00000000..35530c52 --- /dev/null +++ b/src/command/access/init.ts @@ -0,0 +1,67 @@ +import { LeafCommand, Option } from 'furious-commander' +import { exit } from 'process' +import { AccessHistory } from '../../service/access' +import { AccessHistoryOperation } from '../../service/access/types/history-event' +import { createKeyValue } from '../../utils/text' +import { AccessCommand } from './access-command' + +export class Init extends AccessCommand implements LeafCommand { + public readonly name = 'init' + + public readonly description = 'Initialize a new access grantee list' + + @Option({ + key: 'stamp', + alias: 's', + description: 'Postage stamp ID', + required: true, + type: 'string', + }) + public stamp!: string + + @Option({ + key: 'list-name', + alias: 'n', + description: 'Name of the grantee list', + required: true, + type: 'string', + }) + public listName!: string + + @Option({ + key: 'grantee', + alias: 'g', + description: 'Public address of the grantee', + type: 'string', + array: true, + }) + public grantees!: string[] + + public async run(): Promise { + super.init() + + const accessHistory = new AccessHistory(this.commandConfig, this.console) + + if (accessHistory.getEventsByType(this.listName, AccessHistoryOperation.Init).length > 0) { + this.console.error(`Grantee list with name '${this.listName}' has already been initialized!`) + + exit(1) + } + + const response = await this.bee.createGrantees(this.stamp, this.grantees) + this.console.log(`Grantee list '${this.listName}' initialized successfully!`) + + accessHistory.addEvent(this.listName, { + stampId: this.stamp, + historyAddress: response.historyref.toHex(), + granteeListRef: response.ref.toHex(), + operation: AccessHistoryOperation.Init, + createdAt: Date.now(), + }) + + if (this.verbose) { + this.console.log(createKeyValue('Grantee list reference', response.ref.toHex())) + this.console.log(createKeyValue('History address', response.historyref.toHex())) + } + } +} diff --git a/src/command/root-command/command-config.ts b/src/command/root-command/command-config.ts index 4b66619f..9d73432b 100644 --- a/src/command/root-command/command-config.ts +++ b/src/command/root-command/command-config.ts @@ -1,4 +1,4 @@ -import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'fs' +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs' import { homedir, platform } from 'os' import { join } from 'path' import { exit } from 'process' @@ -54,6 +54,10 @@ export class CommandConfig { return true } + public getAccessHistoryFilePath(): string { + return process.env.SWARM_CLI_ACCESS_HISTORY_FILE_PATH || join(this.configFolderPath, 'access-history.json') + } + public getHistoryFilePath(): string { return process.env.SWARM_CLI_HISTORY_FILE_PATH || join(this.configFolderPath, 'upload-history.json') } diff --git a/src/config.ts b/src/config.ts index 13683d20..c0929790 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,11 +1,13 @@ import { IOption } from 'furious-commander' import PackageJson from '../package.json' +import { Access } from './command/access' import { Addresses } from './command/addresses' import { Cheque } from './command/cheque' import { Download } from './command/download' import { Feed } from './command/feed' import { Grantee } from './command/grantee' import { Hash } from './command/hash' +import { History } from './command/history' import { Identity } from './command/identity' import { Manifest } from './command/manifest' import { Pinning } from './command/pinning' @@ -17,7 +19,6 @@ import { Status } from './command/status' import { Upload } from './command/upload' import { Utility } from './command/utility' import { Wallet } from './command/wallet' -import { History } from './command/history' export const beeApiUrl: IOption = { key: 'bee-api-url', @@ -135,4 +136,5 @@ export const rootCommandClasses = [ Grantee, Quickstart, History, + Access, ] diff --git a/src/service/access/index.ts b/src/service/access/index.ts new file mode 100644 index 00000000..63d66a90 --- /dev/null +++ b/src/service/access/index.ts @@ -0,0 +1,61 @@ +import { existsSync, readFileSync, writeFileSync } from 'fs' +import { exit } from 'process' +import { CommandConfig } from '../../command/root-command/command-config' +import { CommandLog } from '../../command/root-command/command-log' +import { AccessHistoryEvent, AccessHistoryLog, AccessHistoryOperation } from './types/history-event' + +export class AccessHistory { + private commandConfig: CommandConfig + private console: CommandLog + + constructor(commandConfig: CommandConfig, console: CommandLog) { + this.commandConfig = commandConfig + this.console = console + } + + public getHistory(): AccessHistoryLog { + const historyFilePath = this.commandConfig.getAccessHistoryFilePath() + + if (!existsSync(historyFilePath)) { + return {} + } + const historyData = readFileSync(historyFilePath) + try { + const historyLog = JSON.parse(historyData.toString()) as AccessHistoryLog + + return historyLog + } catch (err) { + this.console.error(`There has been an error parsing access history JSON from path: '${historyFilePath}'`) + + exit(1) + } + } + + public getEventsByType(granteeListName: string, eventType: AccessHistoryOperation): AccessHistoryEvent[] { + const history = this.getHistory() + + if (!history[granteeListName]) { + return [] + } + + return history[granteeListName].filter(event => event.operation === eventType) + } + + public addEvent(granteeListName: string, event: AccessHistoryEvent) { + const history = this.getHistory() + + if (!history[granteeListName]) { + history[granteeListName] = [] + } + + history[granteeListName].push({ + stampId: event.stampId, + historyAddress: event.historyAddress, + granteeListRef: event.granteeListRef, + operation: event.operation, + createdAt: event.createdAt, + }) + + writeFileSync(this.commandConfig.getAccessHistoryFilePath(), JSON.stringify(history)) + } +} diff --git a/src/service/access/types/history-event.ts b/src/service/access/types/history-event.ts new file mode 100644 index 00000000..3a6cd30a --- /dev/null +++ b/src/service/access/types/history-event.ts @@ -0,0 +1,15 @@ +export enum AccessHistoryOperation { + Init = 'init', + Grant = 'grant', + Revoke = 'revoke', +} + +export type AccessHistoryEvent = { + stampId: string + historyAddress: string + granteeListRef: string + operation: AccessHistoryOperation + createdAt: number +} + +export type AccessHistoryLog = { [name: string]: AccessHistoryEvent[] } diff --git a/test/command/access.spec.ts b/test/command/access.spec.ts new file mode 100644 index 00000000..940f18fe --- /dev/null +++ b/test/command/access.spec.ts @@ -0,0 +1,38 @@ +import { unlinkSync } from 'fs' +import { describeCommand, invokeTestCli } from '../utility' +import { getPssAddress } from '../utility/address' +import { getStampOption } from '../utility/stamp' + +describeCommand( + 'Test Access command', + ({ consoleMessages, getLastMessage }) => { + afterEach(() => { + unlinkSync(`${__dirname}/../testconfig/access-access-history.json`) + }) + describe('init', () => { + it('should initialize access with pss address as grantee', async () => { + const pssAddress = await getPssAddress('http://localhost:21633') + await invokeTestCli([ + 'access', + 'init', + ...getStampOption(), + '--list-name', + 'test-access', + '--grantee', + pssAddress.toHex(), + ]) + expect(getLastMessage()).toEqual("Grantee list 'test-access' initialized successfully!") + }) + + describe('when grantee list with the same name already exists', () => { + it('should show error message', async () => { + await invokeTestCli(['access', 'init', ...getStampOption(), '-n', 'test-access']) + await invokeTestCli(['access', 'init', ...getStampOption(), '-n', 'test-access']) + expect(consoleMessages[1]).toEqual("Grantee list with name 'test-access' has already been initialized!") + expect(consoleMessages[2]).toContain('process.exit() was called with code 1') + }) + }) + }) + }, + { configFileName: 'access' }, +) diff --git a/test/command/create-batch.spec.ts b/test/command/create-batch.spec.ts index f9d40394..f8eefe72 100644 --- a/test/command/create-batch.spec.ts +++ b/test/command/create-batch.spec.ts @@ -5,22 +5,25 @@ expect.extend({ toMatchLinesInOrder, }) +const FUNDER_KEY = '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba' +const JSON_RPC_URL = 'http://localhost:8545' + describeCommand('Test `utility create-batch` command', ({ consoleMessages }) => { it('should create batch', async () => { - process.env.SWARM_CLI_NETWORK_ID = '4020' - process.env.SWARM_CLI_BZZ_ADDRESS = '0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab' - process.env.SWARM_CLI_POSTAGE_STAMP_ADDRESS = '0x254dffcd3277C0b1660F6d42EFbB754edaBAbC2B' + process.env.SWARM_CLI_NETWORK_ID = '1337' + process.env.SWARM_CLI_BZZ_ADDRESS = '0x5FbDB2315678afecb367f032d93F642f64180aa3' + process.env.SWARM_CLI_POSTAGE_STAMP_ADDRESS = '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512' await invokeTestCli([ 'utility', 'create-batch', '--private-key', - '0x566058308ad5fa3888173c741a1fb902c9f1f19559b11fc2738dfc53637ce4e9', + FUNDER_KEY, '--depth', '17', '--amount', '10B', '--json-rpc-url', - 'http://localhost:9545', + JSON_RPC_URL, '--yes', ]) expect(consoleMessages).toMatchLinesInOrder([ diff --git a/test/command/redeem.spec.ts b/test/command/redeem.spec.ts index 09576b2b..fbc8272c 100644 --- a/test/command/redeem.spec.ts +++ b/test/command/redeem.spec.ts @@ -7,15 +7,15 @@ expect.extend({ toMatchLinesInOrder, }) -const FUNDER_KEY = '0x566058308ad5fa3888173c741a1fb902c9f1f19559b11fc2738dfc53637ce4e9' -const JSON_RPC_URL = 'http://localhost:9545' +const FUNDER_KEY = '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba' +const JSON_RPC_URL = 'http://localhost:8545' const ETH_01 = 100_000_000_000_000_000n.toString() const BZZ_5 = 50_000_000_000_000_000n.toString() describeCommand('Test `utility redeem` command', ({ consoleMessages }) => { it('should redeem funds to target address', async () => { - process.env.SWARM_CLI_NETWORK_ID = '4020' - process.env.SWARM_CLI_BZZ_ADDRESS = '0xe78A0F7E598Cc8b0Bb87894B0F60dD2a88d6a8Ab' + process.env.SWARM_CLI_NETWORK_ID = '1337' + process.env.SWARM_CLI_BZZ_ADDRESS = '0x5FbDB2315678afecb367f032d93F642f64180aa3' const sourceWallet = Wallet.createRandom() const targetWallet = Wallet.createRandom() diff --git a/test/command/stamp.spec.ts b/test/command/stamp.spec.ts index afc7c193..f7bcfeea 100644 --- a/test/command/stamp.spec.ts +++ b/test/command/stamp.spec.ts @@ -1,10 +1,10 @@ +import { BatchId, Bee } from '@ethersphere/bee-js' import { Types } from 'cafe-utility' +import { randomBytes } from 'crypto' import inquirer from 'inquirer' import { Buy } from '../../src/command/stamp/buy' import { toMatchLinesInOrder } from '../custom-matcher' import { describeCommand, invokeTestCli } from '../utility' -import { BatchId, Bee } from '@ethersphere/bee-js' -import { randomBytes } from 'crypto' expect.extend({ toMatchLinesInOrder, @@ -105,7 +105,7 @@ describeCommand( }) it('should print custom message when there are no stamps', async () => { - await invokeTestCli(['stamp', 'list', '--bee-api-url', 'http://localhost:11633']) + await invokeTestCli(['stamp', 'list', '--bee-api-url', 'http://localhost:21633']) expect(getNthLastMessage(4)).toContain('You do not have any stamps.') }) diff --git a/test/utility/index.ts b/test/utility/index.ts index e96e5d9d..c4736629 100644 --- a/test/utility/index.ts +++ b/test/utility/index.ts @@ -66,8 +66,8 @@ export function describeCommand( return true }) - jest.spyOn(process, 'exit').mockImplementation(() => { - throw new Error('process.exit() was called.') + jest.spyOn(process, 'exit').mockImplementation(code => { + throw new Error(`process.exit() was called with code ${code}`) }) jest.spyOn(global.console, 'warn') @@ -77,6 +77,12 @@ export function describeCommand( configFileName ? `${configFileName}-upload-history.json` : 'upload-history.json', ) process.env.SWARM_CLI_HISTORY_FILE_PATH = historyFilePath + + const accessHistoryFilePath = join( + configFolderPath, + configFileName ? `${configFileName}-access-history.json` : 'access-history.json', + ) + process.env.SWARM_CLI_ACCESS_HISTORY_FILE_PATH = accessHistoryFilePath process.env.SWARM_CLI_VERSION_CHECK_FILE_PATH = join(configFolderPath, 'version-check.json') //if own config is needed