From 078479c5abf8a44bf94f07dda5d42c85046993c2 Mon Sep 17 00:00:00 2001 From: Igor Shadurin Date: Thu, 27 Jul 2023 11:48:14 +0300 Subject: [PATCH 1/3] fix: feed type configurable --- src/account/account-data.ts | 9 ++- src/account/index.ts | 1 - src/account/mnemonic.ts | 21 ++++-- src/cache/types.ts | 2 +- src/cache/utils.ts | 2 +- src/content-items/handler.ts | 78 +++++++++++++------- src/content-items/types.ts | 11 ++- src/content-items/utils.ts | 24 ++++-- src/directory/directory.ts | 2 + src/directory/handler.ts | 47 ++++++------ src/feed/api.ts | 77 +++++++++----------- src/feed/{lookup => }/epoch.ts | 38 +++++++++- src/feed/handler.ts | 2 +- src/feed/lookup/linear.ts | 20 ++++- src/feed/lookup/utils.ts | 8 +- src/feed/sequence.ts | 32 ++++++++ src/feed/types.ts | 89 ++++++++++++++++++++++- src/file/file.ts | 19 ++++- src/file/handler.ts | 39 ++++++---- src/pod/api.ts | 8 +- src/pod/cache/api.ts | 5 +- src/pod/personal-storage.ts | 66 ++++++++++++----- src/pod/utils.ts | 74 ++++++++++++++++--- src/types.ts | 5 ++ src/utils/hex.ts | 1 - test/integration/fdp-class.fairos.spec.ts | 14 ++-- test/integration/fdp-class.spec.ts | 28 ++++--- test/unit/feed/epoch.spec.ts | 2 +- test/unit/feed/sequential.spec.ts | 23 ++++++ test/unit/handler.spec.ts | 2 +- 30 files changed, 559 insertions(+), 190 deletions(-) rename src/feed/{lookup => }/epoch.ts (73%) create mode 100644 src/feed/sequence.ts create mode 100644 test/unit/feed/sequential.spec.ts diff --git a/src/account/account-data.ts b/src/account/account-data.ts index f1ccec29..b40fdf6c 100644 --- a/src/account/account-data.ts +++ b/src/account/account-data.ts @@ -11,6 +11,7 @@ import CryptoJS from 'crypto-js' import { bytesToHex } from '../utils/hex' import { mnemonicToSeed, prepareEthAddress, privateKeyToBytes } from '../utils/wallet' import { isChunkAlreadyExistsError, isInsufficientFundsError } from '../utils/error' +import { FeedType } from '../feed/types' export class AccountData { /** @@ -95,7 +96,13 @@ export class AccountData { if (isAddressOptions(options)) { const address = prepareEthAddress(options.address) - const encryptedMnemonic = await getEncryptedMnemonic(this.connection.bee, username, address) + const encryptedMnemonic = await getEncryptedMnemonic( + this.connection.bee, + username, + address, + this.connection.options?.feedType ?? FeedType.Epoch, + this.connection.options?.requestOptions, + ) mnemonic = decryptText(password, encryptedMnemonic) } diff --git a/src/account/index.ts b/src/account/index.ts index e62083bb..69065539 100644 --- a/src/account/index.ts +++ b/src/account/index.ts @@ -1,4 +1,3 @@ export * as Account from './account' export * as Encryption from '../utils/encryption' export * as Utils from './utils' -//TODO export every exportable diff --git a/src/account/mnemonic.ts b/src/account/mnemonic.ts index c453f290..837fa923 100644 --- a/src/account/mnemonic.ts +++ b/src/account/mnemonic.ts @@ -1,9 +1,11 @@ -import { Bee, Reference, Utils } from '@ethersphere/bee-js' +import { Bee, BeeRequestOptions, Reference, Utils } from '@ethersphere/bee-js' import { assertBase64UrlData, assertUsername } from './utils' import { Wallet } from 'ethers' -import { getFeedData, writeFeedDataRaw } from '../feed/api' +import { getFeedData } from '../feed/api' import { Connection } from '../connection/connection' import { stringToBytes } from '../utils/bytes' +import { writeEpochFeedDataRaw } from '../feed/epoch' +import { FeedType } from '../feed/types' /** * Downloads encrypted mnemonic phrase from swarm chunk for version 1 account @@ -13,13 +15,20 @@ import { stringToBytes } from '../utils/bytes' * @param bee Bee client * @param username FDP account username * @param address FDP account address - * + * @param feedType + * @param requestOptions download data requestOptions * @returns encrypted mnemonic phrase in Base64url format */ -export async function getEncryptedMnemonic(bee: Bee, username: string, address: Utils.EthAddress): Promise { +export async function getEncryptedMnemonic( + bee: Bee, + username: string, + address: Utils.EthAddress, + feedType: FeedType, + requestOptions?: BeeRequestOptions, +): Promise { assertUsername(username) - return (await getFeedData(bee, username, address)).data.chunkContent().text() + return (await getFeedData(bee, username, address, feedType, requestOptions)).data.chunkContent().text() } /** @@ -41,5 +50,5 @@ export async function uploadEncryptedMnemonic( assertUsername(username) assertBase64UrlData(encryptedMnemonic) - return writeFeedDataRaw(connection, username, stringToBytes(encryptedMnemonic), wallet) + return writeEpochFeedDataRaw(connection, username, stringToBytes(encryptedMnemonic), wallet) } diff --git a/src/cache/types.ts b/src/cache/types.ts index 4d3bc23a..b88470d5 100644 --- a/src/cache/types.ts +++ b/src/cache/types.ts @@ -1,4 +1,4 @@ -import { Epoch } from '../feed/lookup/epoch' +import { Epoch } from '../feed/epoch' export const DEFAULT_CACHE_OPTIONS: CacheOptions = { isUseCache: true, diff --git a/src/cache/utils.ts b/src/cache/utils.ts index f21c4db3..b90043aa 100644 --- a/src/cache/utils.ts +++ b/src/cache/utils.ts @@ -1,6 +1,6 @@ import CryptoJS from 'crypto-js' import { CacheEpochData, CacheEpochDataPrepared, CacheInfo, CacheObject, ProcessCacheDataOptions } from './types' -import { Epoch } from '../feed/lookup/epoch' +import { Epoch } from '../feed/epoch' /** * Creates cache key diff --git a/src/content-items/handler.ts b/src/content-items/handler.ts index 62e0bffe..7bde2cae 100644 --- a/src/content-items/handler.ts +++ b/src/content-items/handler.ts @@ -1,6 +1,6 @@ import { Connection } from '../connection/connection' import { utils } from 'ethers' -import { Reference, BeeRequestOptions } from '@ethersphere/bee-js' +import { BeeRequestOptions, Reference } from '@ethersphere/bee-js' import { getUnixTimestamp } from '../utils/time' import { writeFeedData } from '../feed/api' import { getRawDirectoryMetadataBytes } from '../directory/adapter' @@ -12,7 +12,7 @@ import { RawMetadataWithEpoch } from './types' import { prepareEthAddress } from '../utils/wallet' import { PodPasswordBytes } from '../utils/encryption' import { DataUploadOptions } from '../file/types' -import { getNextEpoch } from '../feed/lookup/utils' +import { FeedType, WriteFeedOptions } from '../feed/types' export const DEFAULT_UPLOAD_OPTIONS: DataUploadOptions = { blockSize: 1000000, @@ -28,6 +28,7 @@ export const DEFAULT_UPLOAD_OPTIONS: DataUploadOptions = { * @param parentPath parent path * @param entryPath entry path * @param isFile define if entry is file or directory + * @param feedType feed type * @param downloadOptions download options */ export async function addEntryToDirectory( @@ -37,6 +38,7 @@ export async function addEntryToDirectory( parentPath: string, entryPath: string, isFile: boolean, + feedType: FeedType, downloadOptions?: BeeRequestOptions, ): Promise { if (!parentPath) { @@ -54,7 +56,14 @@ export async function addEntryToDirectory( let parentData: RawDirectoryMetadata | undefined let metadataWithEpoch: RawMetadataWithEpoch | undefined try { - metadataWithEpoch = await getRawMetadata(connection.bee, parentPath, address, podPassword, downloadOptions) + metadataWithEpoch = await getRawMetadata( + connection.bee, + parentPath, + address, + podPassword, + connection.options?.feedType ?? FeedType.Epoch, + downloadOptions, + ) assertRawDirectoryMetadata(metadataWithEpoch.metadata) parentData = metadataWithEpoch.metadata } catch (e) { @@ -71,14 +80,13 @@ export async function addEntryToDirectory( parentData.fileOrDirNames.push(itemToAdd) parentData.meta.modificationTime = getUnixTimestamp() - return writeFeedData( - connection, - parentPath, - getRawDirectoryMetadataBytes(parentData), - wallet, - podPassword, - getNextEpoch(metadataWithEpoch.epoch), - ) + return writeFeedData(connection, parentPath, getRawDirectoryMetadataBytes(parentData), wallet, podPassword, { + feedType, + epochOptions: { + epoch: metadataWithEpoch.epoch, + isGetNextEpoch: true, + }, + }) } /** @@ -91,6 +99,7 @@ export async function deleteItem( itemMetaPath: string, wallet: utils.HDNode, podPassword: PodPasswordBytes, + writeFeedOptions: WriteFeedOptions, ): Promise { let pathInfo try { @@ -98,6 +107,7 @@ export async function deleteItem( connection.bee, itemMetaPath, prepareEthAddress(wallet.address), + connection.options?.feedType ?? FeedType.Epoch, connection.options?.requestOptions, ) // eslint-disable-next-line no-empty @@ -108,15 +118,24 @@ export async function deleteItem( return } - let metaPathEpoch - - // if information is stored under the path, calculate the next level of epoch - if (pathInfo) { - pathInfo.lookupAnswer.epoch.level = pathInfo.lookupAnswer.epoch.getNextLevel(pathInfo.lookupAnswer.epoch.time) - metaPathEpoch = pathInfo.lookupAnswer.epoch + // for epoch feed additionally calculate the next epoch level + if (writeFeedOptions.feedType === FeedType.Epoch) { + // if information is stored under the path, calculate the next level of epoch + if (pathInfo) { + if (!pathInfo.lookupAnswer.epoch) { + throw new Error('Epoch is not defined') + } + + if (!writeFeedOptions.epochOptions) { + writeFeedOptions.epochOptions = {} + } + + writeFeedOptions.epochOptions.epoch = pathInfo.lookupAnswer.epoch + writeFeedOptions.epochOptions.isGetNextLevel = true + } } - await deleteFeedData(connection, itemMetaPath, wallet, podPassword, metaPathEpoch) + await deleteFeedData(connection, itemMetaPath, wallet, podPassword, writeFeedOptions) } /** @@ -139,11 +158,13 @@ export async function removeEntryFromDirectory( isFile: boolean, downloadOptions?: BeeRequestOptions, ): Promise { + const feedType = connection.options?.feedType ?? FeedType.Epoch const metadataWithEpoch = await getRawMetadata( connection.bee, parentPath, prepareEthAddress(wallet.address), podPassword, + connection.options?.feedType ?? FeedType.Epoch, downloadOptions, ) const parentData = metadataWithEpoch.metadata @@ -158,14 +179,15 @@ export async function removeEntryFromDirectory( } parentData.fileOrDirNames = fileOrDirNames.filter(name => name !== itemToRemove) - await deleteItem(connection, fullPath, wallet, podPassword) - - return writeFeedData( - connection, - parentPath, - getRawDirectoryMetadataBytes(parentData), - wallet, - podPassword, - getNextEpoch(metadataWithEpoch.epoch), - ) + await deleteItem(connection, fullPath, wallet, podPassword, { + feedType, + }) + + return writeFeedData(connection, parentPath, getRawDirectoryMetadataBytes(parentData), wallet, podPassword, { + feedType, + epochOptions: { + epoch: metadataWithEpoch.epoch, + isGetNextEpoch: true, + }, + }) } diff --git a/src/content-items/types.ts b/src/content-items/types.ts index 13960a01..2bf0721c 100644 --- a/src/content-items/types.ts +++ b/src/content-items/types.ts @@ -1,4 +1,4 @@ -import { Epoch } from '../feed/lookup/epoch' +import { Epoch } from '../feed/epoch' import { RawDirectoryMetadata, RawFileMetadata } from '../pod/types' import { BeeRequestOptions } from '@ethersphere/bee-js' import { CacheInfo } from '../cache/types' @@ -26,8 +26,15 @@ export interface DownloadOptions { * Metadata of a file or directory with epoch */ export interface RawMetadataWithEpoch { - epoch: Epoch + /** + * Metadata of a file or directory + */ metadata: RawDirectoryMetadata | RawFileMetadata + + /** + * Epoch of the metadata from the epoch feed + */ + epoch?: Epoch } /** diff --git a/src/content-items/utils.ts b/src/content-items/utils.ts index 915a81f3..10c54e63 100644 --- a/src/content-items/utils.ts +++ b/src/content-items/utils.ts @@ -9,8 +9,8 @@ import CryptoJS from 'crypto-js' import { isObject } from '../utils/type' import { Connection } from '../connection/connection' import { utils, Wallet } from 'ethers' -import { Epoch } from '../feed/lookup/epoch' import { stringToBytes } from '../utils/bytes' +import { FeedType, WriteFeedOptions } from '../feed/types' /** * Get raw metadata by path @@ -19,6 +19,7 @@ import { stringToBytes } from '../utils/bytes' * @param path path with information * @param address Ethereum address of the pod which owns the path * @param podPassword bytes for data encryption from pod metadata + * @param feedType feed type * @param requestOptions options for downloading */ export async function getRawMetadata( @@ -26,9 +27,10 @@ export async function getRawMetadata( path: string, address: EthAddress, podPassword: PodPasswordBytes, + feedType: FeedType, requestOptions?: BeeRequestOptions, ): Promise { - const feedData = await getFeedData(bee, path, address, requestOptions) + const feedData = await getFeedData(bee, path, address, feedType, requestOptions) const data = decryptJson(podPassword, feedData.data.chunkContent()) let metadata @@ -52,16 +54,18 @@ export async function getRawMetadata( * @param bee Bee instance * @param fullPath full path to the item * @param address uploader address + * @param feedType feed type * @param requestOptions options for downloading */ export async function isItemExists( bee: Bee, fullPath: string, address: EthAddress, + feedType: FeedType, requestOptions: BeeRequestOptions | undefined, ): Promise { try { - return (await getFeedData(bee, fullPath, address, requestOptions)).data.text() === DELETE_FEED_MAGIC_WORD + return (await getFeedData(bee, fullPath, address, feedType, requestOptions)).data.text() === DELETE_FEED_MAGIC_WORD } catch (e) { return false } @@ -74,6 +78,7 @@ export async function isItemExists( * @param bee Bee instance * @param fullPath full path to the item * @param address uploader address + * @param feedType feed type * @param downloadOptions options for downloading */ export async function assertItemIsNotExists( @@ -81,9 +86,10 @@ export async function assertItemIsNotExists( bee: Bee, fullPath: string, address: EthAddress, + feedType: FeedType, downloadOptions: BeeRequestOptions | undefined, ): Promise { - if (await isItemExists(bee, fullPath, address, downloadOptions)) { + if (await isItemExists(bee, fullPath, address, feedType, downloadOptions)) { throw new Error(`${contentType} "${fullPath}" already uploaded to the network`) } } @@ -129,9 +135,10 @@ export async function getPathInfo( bee: Bee, path: string, address: EthAddress, + feedType: FeedType, requestOptions?: BeeRequestOptions, ): Promise { - const lookupAnswer = await getFeedData(bee, path, address, requestOptions) + const lookupAnswer = await getFeedData(bee, path, address, feedType, requestOptions) return { isDeleted: lookupAnswer.data.text() === DELETE_FEED_MAGIC_WORD, @@ -160,12 +167,13 @@ export async function getCreationPathInfo( bee: Bee, fullPath: string, address: EthAddress, + feedType: FeedType, requestOptions?: BeeRequestOptions, ): Promise { // check that if directory uploaded - than it should be marked as deleted let pathInfo try { - pathInfo = await getPathInfo(bee, fullPath, address, requestOptions) + pathInfo = await getPathInfo(bee, fullPath, address, feedType, requestOptions) assertItemDeleted(pathInfo, fullPath) // eslint-disable-next-line no-empty } catch (e) {} @@ -181,7 +189,7 @@ export async function deleteFeedData( topic: string, wallet: utils.HDNode | Wallet, podPassword: PodPasswordBytes, - epoch?: Epoch, + writeFeedOptions: WriteFeedOptions, ): Promise { - return writeFeedData(connection, topic, stringToBytes(DELETE_FEED_MAGIC_WORD), wallet, podPassword, epoch) + return writeFeedData(connection, topic, stringToBytes(DELETE_FEED_MAGIC_WORD), wallet, podPassword, writeFeedOptions) } diff --git a/src/directory/directory.ts b/src/directory/directory.ts index bd941502..c986c9c5 100644 --- a/src/directory/directory.ts +++ b/src/directory/directory.ts @@ -20,6 +20,7 @@ import { import { uploadData } from '../file/handler' import { assertNodeFileInfo, isBrowserFileInfo } from './types' import { DirectoryItem } from '../content-items/types' +import { FeedType } from '../feed/types' /** * Directory related class @@ -44,6 +45,7 @@ export class Directory { path, podAddress, pod.password, + this.accountData.connection?.options?.feedType ?? FeedType.Epoch, isRecursive, this.accountData.connection.options?.requestOptions, ) diff --git a/src/directory/handler.ts b/src/directory/handler.ts index dc9aa152..48097252 100644 --- a/src/directory/handler.ts +++ b/src/directory/handler.ts @@ -1,6 +1,6 @@ import { writeFeedData } from '../feed/api' import { EthAddress } from '@ethersphere/bee-js/dist/types/utils/eth' -import { Bee, Reference, BeeRequestOptions } from '@ethersphere/bee-js' +import { Bee, BeeRequestOptions, Reference } from '@ethersphere/bee-js' import { assertDirectoryName, assertPartsLength, @@ -18,17 +18,17 @@ import { Connection } from '../connection/connection' import { utils } from 'ethers' import { addEntryToDirectory, DEFAULT_UPLOAD_OPTIONS } from '../content-items/handler' import { + getCreationPathInfo, + getRawMetadata, rawDirectoryMetadataToDirectoryItem, rawFileMetadataToFileItem, - getRawMetadata, - getCreationPathInfo, } from '../content-items/utils' import { PodPasswordBytes } from '../utils/encryption' import { DataUploadOptions } from '../file/types' import { DirectoryItem } from '../content-items/types' import { prepareEthAddress } from '../utils/wallet' -import { Epoch } from '../feed/lookup/epoch' import { getNextEpoch } from '../feed/lookup/utils' +import { FeedType, WriteFeedOptions } from '../feed/types' /** * Options for uploading a directory @@ -60,6 +60,7 @@ export const DEFAULT_UPLOAD_DIRECTORY_OPTIONS: UploadDirectoryOptions = { * @param path path to start searching from * @param address Ethereum address of the pod which owns the path * @param podPassword bytes for data encryption from pod metadata + * @param feedType type of feed * @param isRecursive search with recursion or not * @param downloadOptions options for downloading */ @@ -68,10 +69,12 @@ export async function readDirectory( path: string, address: EthAddress, podPassword: PodPasswordBytes, + feedType: FeedType, isRecursive?: boolean, downloadOptions?: BeeRequestOptions, ): Promise { - const parentRawDirectoryMetadata = (await getRawMetadata(bee, path, address, podPassword, downloadOptions)).metadata + const parentRawDirectoryMetadata = (await getRawMetadata(bee, path, address, podPassword, feedType, downloadOptions)) + .metadata assertRawDirectoryMetadata(parentRawDirectoryMetadata) const resultDirectoryItem = rawDirectoryMetadataToDirectoryItem(parentRawDirectoryMetadata) @@ -85,17 +88,17 @@ export async function readDirectory( if (isFile) { item = combine(...splitPath(path), item.substring(FILE_TOKEN.length)) - const data = (await getRawMetadata(bee, item, address, podPassword, downloadOptions)).metadata + const data = (await getRawMetadata(bee, item, address, podPassword, feedType, downloadOptions)).metadata assertRawFileMetadata(data) resultDirectoryItem.files.push(rawFileMetadataToFileItem(data)) } else if (isDirectory) { item = combine(...splitPath(path), item.substring(DIRECTORY_TOKEN.length)) - const data = (await getRawMetadata(bee, item, address, podPassword, downloadOptions)).metadata + const data = (await getRawMetadata(bee, item, address, podPassword, feedType, downloadOptions)).metadata assertRawDirectoryMetadata(data) const currentMetadata = rawDirectoryMetadataToDirectoryItem(data) if (isRecursive) { - const content = await readDirectory(bee, item, address, podPassword, isRecursive, downloadOptions) + const content = await readDirectory(bee, item, address, podPassword, feedType, isRecursive, downloadOptions) currentMetadata.files = content.files currentMetadata.directories = content.directories } @@ -115,7 +118,7 @@ export async function readDirectory( * @param name name of the directory * @param podPassword bytes for data encryption from pod metadata * @param wallet feed owner's wallet - * @param epoch epoch where directory info should be uploaded + * @param writeFeedOptions options for writing to feed */ async function createDirectoryInfo( connection: Connection, @@ -123,12 +126,12 @@ async function createDirectoryInfo( name: string, podPassword: PodPasswordBytes, wallet: utils.HDNode, - epoch?: Epoch, + writeFeedOptions: WriteFeedOptions, ): Promise { const now = getUnixTimestamp() const metadata = createRawDirectoryMetadata(META_VERSION, path, name, now, now, now) - return writeFeedData(connection, combine(...splitPath(path), name), metadata, wallet, podPassword, epoch) + return writeFeedData(connection, combine(...splitPath(path), name), metadata, wallet, podPassword, writeFeedOptions) } /** @@ -137,13 +140,15 @@ async function createDirectoryInfo( * @param connection Bee connection * @param podPassword bytes for data encryption * @param wallet feed owner's wallet + * @param writeFeedOptions options for writing to feed */ export async function createRootDirectory( connection: Connection, podPassword: PodPasswordBytes, wallet: utils.HDNode, + writeFeedOptions: WriteFeedOptions, ): Promise { - return createDirectoryInfo(connection, '', '/', podPassword, wallet) + return createDirectoryInfo(connection, '', '/', podPassword, wallet, writeFeedOptions) } /** @@ -168,19 +173,19 @@ export async function createDirectory( assertDirectoryName(name) const parentPath = getPathFromParts(parts, 1) + const feedType = connection.options?.feedType ?? FeedType.Epoch const pathInfo = await getCreationPathInfo( connection.bee, fullPath, prepareEthAddress(podWallet.address), + feedType, connection.options?.requestOptions, ) - await addEntryToDirectory(connection, podWallet, podPassword, parentPath, name, false, downloadOptions) - await createDirectoryInfo( - connection, - parentPath, - name, - podPassword, - podWallet, - getNextEpoch(pathInfo?.lookupAnswer.epoch), - ) + await addEntryToDirectory(connection, podWallet, podPassword, parentPath, name, false, feedType, downloadOptions) + const epoch = pathInfo?.lookupAnswer.epoch + const writeFeedOptions: WriteFeedOptions = { + feedType, + ...(feedType === FeedType.Epoch && epoch ? { epochOptions: { epoch: getNextEpoch(epoch) } } : {}), + } + await createDirectoryInfo(connection, parentPath, name, podPassword, podWallet, writeFeedOptions) } diff --git a/src/feed/api.ts b/src/feed/api.ts index c4b54e7d..1f403abd 100644 --- a/src/feed/api.ts +++ b/src/feed/api.ts @@ -1,14 +1,13 @@ -import { Bee, Data, Reference, BeeRequestOptions, Utils } from '@ethersphere/bee-js' +import { Bee, BeeRequestOptions, Reference, Utils } from '@ethersphere/bee-js' import { bmtHashString } from '../account/utils' -import { getId } from './handler' -import { lookup } from './lookup/linear' -import { Epoch, HIGHEST_LEVEL } from './lookup/epoch' -import { bytesToHex } from '../utils/hex' -import { getUnixTimestamp } from '../utils/time' -import { LookupAnswer } from './types' +import { writeEpochFeedDataRaw } from './epoch' +import { assertWriteFeedOptions, FeedType, LookupAnswer, WriteFeedOptions } from './types' import { Connection } from '../connection/connection' import { encryptBytes, PodPasswordBytes } from '../utils/encryption' import { utils, Wallet } from 'ethers' +import { writeSequenceFeedData } from './sequence' +import { lookupWithEpoch } from './lookup/linear' +import { getNextEpoch } from './lookup/utils' /** * Magic word for replacing content after deletion @@ -21,22 +20,26 @@ export const DELETE_FEED_MAGIC_WORD = '__Fair__' * @param bee Bee client * @param topic topic for calculation swarm chunk * @param address Ethereum address for calculation swarm chunk + * @param feedType feed type * @param requestOptions download chunk requestOptions */ export async function getFeedData( bee: Bee, topic: string, address: Utils.EthAddress | Uint8Array, + feedType: FeedType, requestOptions?: BeeRequestOptions, ): Promise { const topicHash = bmtHashString(topic) - return lookup(0, async (epoch: Epoch, time: number): Promise => { - const tempId = getId(topicHash, time, epoch.level) - const chunkReference = bytesToHex(Utils.keccak256Hash(tempId.buffer, address.buffer)) - - return bee.downloadChunk(chunkReference, requestOptions) - }) + if (feedType === FeedType.Epoch) { + return lookupWithEpoch(bee, topicHash, address, requestOptions) + } else if (feedType === FeedType.Sequence) { + // todo implement with sequence feed reader + throw new Error('Sequence feed not implemented yet') + } else { + throw new Error('Unknown feed type') + } } /** @@ -47,7 +50,7 @@ export async function getFeedData( * @param data data to upload * @param wallet feed owner's wallet * @param podPassword bytes for data encryption from pod metadata - * @param epoch feed epoch + * @param options write feed options */ export async function writeFeedData( connection: Connection, @@ -55,38 +58,28 @@ export async function writeFeedData( data: Uint8Array, wallet: utils.HDNode | Wallet, podPassword: PodPasswordBytes, - epoch?: Epoch, + options: WriteFeedOptions, ): Promise { + assertWriteFeedOptions(options) data = encryptBytes(podPassword, data) - return writeFeedDataRaw(connection, topic, data, wallet, epoch) -} + let epoch = options.epochOptions?.epoch -/** - * Writes data without encryption to feed using `topic` and `epoch` as a key and signed data with `privateKey` as a value - * - * @deprecated required for deprecated methods - * - * @param connection connection information for data uploading - * @param topic key for data - * @param data data to upload - * @param wallet feed owner's wallet - * @param epoch feed epoch - */ -export async function writeFeedDataRaw( - connection: Connection, - topic: string, - data: Uint8Array, - wallet: utils.HDNode | Wallet, - epoch?: Epoch, -): Promise { - if (!epoch) { - epoch = new Epoch(HIGHEST_LEVEL, getUnixTimestamp()) - } + if (options.feedType === FeedType.Epoch && epoch) { + if (options.epochOptions?.isGetNextEpoch) { + epoch = getNextEpoch(epoch) + } - const topicHash = bmtHashString(topic) - const id = getId(topicHash, epoch.time, epoch.level) - const socWriter = connection.bee.makeSOCWriter(wallet.privateKey) + if (options.epochOptions?.isGetNextLevel) { + epoch.level = epoch.getNextLevel(epoch.time) + } + } - return socWriter.upload(connection.postageBatchId, id, data) + if (options.feedType === FeedType.Epoch) { + return writeEpochFeedDataRaw(connection, topic, data, wallet, epoch) + } else if (options.feedType === FeedType.Sequence) { + return writeSequenceFeedData(connection, topic, data, wallet) + } else { + throw new Error('Unknown feed type') + } } diff --git a/src/feed/lookup/epoch.ts b/src/feed/epoch.ts similarity index 73% rename from src/feed/lookup/epoch.ts rename to src/feed/epoch.ts index a22d07f8..e7e390e9 100644 --- a/src/feed/lookup/epoch.ts +++ b/src/feed/epoch.ts @@ -1,5 +1,10 @@ -import { Utils } from '@ethersphere/bee-js' -import { longToByteArray } from '../../utils/bytes' +import { Reference, Utils } from '@ethersphere/bee-js' +import { longToByteArray } from '../utils/bytes' +import { Connection } from '../connection/connection' +import { utils, Wallet } from 'ethers' +import { getUnixTimestamp } from '../utils/time' +import { bmtHashString } from '../account/utils' +import { getId } from './handler' const EPOCH_LENGTH = 8 @@ -111,3 +116,32 @@ export class Epoch { return new Epoch(this.getNextLevel(time), time) } } + +/** + * Writes data without encryption to feed using `topic` and `epoch` as a key and signed data with `privateKey` as a value + * + * @deprecated required for deprecated methods + * + * @param connection connection information for data uploading + * @param topic key for data + * @param data data to upload + * @param wallet feed owner's wallet + * @param epoch feed epoch + */ +export async function writeEpochFeedDataRaw( + connection: Connection, + topic: string, + data: Uint8Array, + wallet: utils.HDNode | Wallet, + epoch?: Epoch, +): Promise { + if (!epoch) { + epoch = new Epoch(HIGHEST_LEVEL, getUnixTimestamp()) + } + + const topicHash = bmtHashString(topic) + const id = getId(topicHash, epoch.time, epoch.level) + const socWriter = connection.bee.makeSOCWriter(wallet.privateKey) + + return socWriter.upload(connection.postageBatchId, id, data) +} diff --git a/src/feed/handler.ts b/src/feed/handler.ts index ed84fbcb..d2e0ab7a 100644 --- a/src/feed/handler.ts +++ b/src/feed/handler.ts @@ -1,6 +1,6 @@ import { Utils } from '@ethersphere/bee-js' import { makeContentAddressedChunk } from '../chunk/cac' -import { Epoch, HIGHEST_LEVEL } from './lookup/epoch' +import { Epoch, HIGHEST_LEVEL } from './epoch' const TOPIC_LENGTH = 32 diff --git a/src/feed/lookup/linear.ts b/src/feed/lookup/linear.ts index 7dff3b68..9b01411b 100644 --- a/src/feed/lookup/linear.ts +++ b/src/feed/lookup/linear.ts @@ -1,9 +1,11 @@ -import { Epoch, HIGHEST_LEVEL } from './epoch' -import { Data } from '@ethersphere/bee-js' +import { Epoch, HIGHEST_LEVEL } from '../epoch' +import { Bee, BeeRequestOptions, Data, Utils } from '@ethersphere/bee-js' import { getUnixTimestamp } from '../../utils/time' import { wrapChunkHelper } from '../utils' import { LookupAnswer } from '../types' import { assertUnixTimestamp } from '../../utils/time' +import { getId } from '../handler' +import { bytesToHex } from '../../utils/hex' /** * Searches feed content with linear way @@ -49,3 +51,17 @@ export async function lookup(time: number, read: (epoch: Epoch, time: number) => return { data: wrapChunkHelper(previousChunk), epoch: previousEpoch ? previousEpoch : epoch } } + +export async function lookupWithEpoch( + bee: Bee, + topicHash: Utils.Bytes<32>, + address: Utils.EthAddress | Uint8Array, + requestOptions?: BeeRequestOptions, +): Promise { + return lookup(0, async (epoch: Epoch, time: number): Promise => { + const tempId = getId(topicHash, time, epoch.level) + const chunkReference = bytesToHex(Utils.keccak256Hash(tempId.buffer, address.buffer)) + + return bee.downloadChunk(chunkReference, requestOptions) + }) +} diff --git a/src/feed/lookup/utils.ts b/src/feed/lookup/utils.ts index 6cd9c21f..1da1cfd7 100644 --- a/src/feed/lookup/utils.ts +++ b/src/feed/lookup/utils.ts @@ -1,9 +1,9 @@ -import { Epoch } from './epoch' +import { Epoch } from '../epoch' import { getUnixTimestamp } from '../../utils/time' /** - * Gets next epoch if epoch is defined + * Gets next epoch */ -export function getNextEpoch(epoch: Epoch | undefined): Epoch | undefined { - return epoch ? epoch.getNextEpoch(getUnixTimestamp()) : undefined +export function getNextEpoch(epoch: Epoch): Epoch { + return epoch.getNextEpoch(getUnixTimestamp()) } diff --git a/src/feed/sequence.ts b/src/feed/sequence.ts new file mode 100644 index 00000000..0de7eb1f --- /dev/null +++ b/src/feed/sequence.ts @@ -0,0 +1,32 @@ +import { Connection } from '../connection/connection' +import { utils, Wallet } from 'ethers' +import { Reference } from '@ethersphere/bee-js' +import { bmtHashString } from '../account/utils' +import { FeedType } from './types' + +/** + * Writes data to sequence feed + * + * @param connection connection information for data uploading + * @param topic key for data + * @param data data to upload + * @param wallet feed owner's wallet + */ +export async function writeSequenceFeedData( + connection: Connection, + topic: string, + data: Uint8Array, + wallet: utils.HDNode | Wallet, +): Promise { + const topicHash = bmtHashString(topic) + const feedWriter = connection.bee.makeFeedWriter( + FeedType.Epoch, + topicHash, + wallet.privateKey, + connection.options?.requestOptions, + ) + + const dataReference = await connection.bee.uploadData(connection.postageBatchId, data) + + return feedWriter.upload(connection.postageBatchId, dataReference.reference) +} diff --git a/src/feed/types.ts b/src/feed/types.ts index e4ab0d22..74b3f56b 100644 --- a/src/feed/types.ts +++ b/src/feed/types.ts @@ -1,5 +1,5 @@ import { Data } from '@ethersphere/bee-js' -import { Epoch } from './lookup/epoch' +import { Epoch } from './epoch' /** * Lookup data with possibility to get chunk content @@ -12,6 +12,91 @@ export interface LookupData extends Data { * Result information for lookup methods with extended information */ export interface LookupAnswer { + /** + * Data from lookup + */ data: LookupData - epoch: Epoch + + /** + * Epoch info only for epoch feed + */ + epoch?: Epoch +} + +/** + * Feed type + */ +export enum FeedType { + Sequence = 'sequence', + Epoch = 'epoch', +} + +/** + * Epoch options + */ +export interface EpochOptions { + /** + * Current epoch + */ + epoch?: Epoch + + /** + * Get next epoch or not + */ + isGetNextEpoch?: boolean + + /** + * Get next level or not + */ + isGetNextLevel?: boolean +} + +/** + * Write feed options + */ +export interface WriteFeedOptions { + /** + * Feed type + */ + feedType: FeedType + + /** + * Epoch options + */ + epochOptions?: EpochOptions +} + +/** + * Asserts that data is an epoch options + * + * @param data Data to check + */ +export function assertEpochOptions(data: unknown): asserts data is EpochOptions { + const options = data as EpochOptions + + if (options?.epoch === undefined && options?.isGetNextEpoch === undefined) { + throw new Error('EpochOptions: Neither "epoch" nor "isGetNextEpoch" is defined') + } +} + +/** + * Asserts that data is a write feed options + * + * @param data Data to check + */ +export function assertWriteFeedOptions(data: unknown): asserts data is WriteFeedOptions { + const options = data as WriteFeedOptions + + switch (options.feedType) { + case FeedType.Epoch: + break + case FeedType.Sequence: + if (options.epochOptions) { + throw new Error('WriteFeedOptions: "epochOptions" is defined for "sequence" feed type') + } + + break + default: + throw new Error('WriteFeedOptions: "feedType" is not valid') + } } diff --git a/src/file/file.ts b/src/file/file.ts index 39580ecc..b6e4eb8f 100644 --- a/src/file/file.ts +++ b/src/file/file.ts @@ -20,6 +20,7 @@ import { Reference } from '@ethersphere/bee-js' import { getRawMetadata } from '../content-items/utils' import { assertRawFileMetadata, combine, splitPath } from '../directory/utils' import { assertEncryptedReference, EncryptedReference } from '../utils/hex' +import { FeedType } from '../feed/types' /** * Files management class @@ -45,6 +46,7 @@ export class File { fullPath, podAddress, pod.password, + this.accountData.connection.options?.feedType ?? FeedType.Epoch, this.accountData.connection.options?.requestOptions, ) } @@ -104,7 +106,15 @@ export class File { const connection = this.accountData.connection const { podAddress, pod } = await getExtendedPodsListByAccountData(this.accountData, podName) - const meta = (await getRawMetadata(connection.bee, fullPath, podAddress, pod.password)).metadata + const meta = ( + await getRawMetadata( + connection.bee, + fullPath, + podAddress, + pod.password, + this.accountData.connection.options?.feedType ?? FeedType.Epoch, + ) + ).metadata assertRawFileMetadata(meta) const data = JSON.stringify(createFileShareInfo(meta)) @@ -149,8 +159,11 @@ export class File { const fileName = options?.name ?? sharedInfo.meta.fileName meta = updateFileMetadata(meta, parentPath, fileName) const fullPath = combine(...splitPath(parentPath), fileName) - await addEntryToDirectory(connection, podWallet, pod.password, parentPath, fileName, true) - await writeFeedData(connection, fullPath, getFileMetadataRawBytes(meta), podWallet, pod.password) + const feedType = this.accountData.connection.options?.feedType ?? FeedType.Epoch + await addEntryToDirectory(connection, podWallet, pod.password, parentPath, fileName, true, feedType) + await writeFeedData(connection, fullPath, getFileMetadataRawBytes(meta), podWallet, pod.password, { + feedType, + }) return meta } diff --git a/src/file/handler.ts b/src/file/handler.ts index 415b68b6..705ee5f1 100644 --- a/src/file/handler.ts +++ b/src/file/handler.ts @@ -1,5 +1,5 @@ import { stringToBytes, wrapBytesWithHelpers } from '../utils/bytes' -import { Bee, Data, BeeRequestOptions } from '@ethersphere/bee-js' +import { Bee, BeeRequestOptions, Data } from '@ethersphere/bee-js' import { EthAddress } from '@ethersphere/bee-js/dist/types/utils/eth' import { assertFullPathWithName, @@ -22,7 +22,7 @@ import { writeFeedData } from '../feed/api' import { AccountData } from '../account/account-data' import { prepareEthAddress } from '../utils/wallet' import { assertWallet } from '../utils/type' -import { getNextEpoch } from '../feed/lookup/utils' +import { FeedType, WriteFeedOptions } from '../feed/types' /** * File prefix @@ -40,6 +40,7 @@ export const DIRECTORY_TOKEN = '_D_' * @param path path with information * @param address Ethereum address of the pod which contains the path * @param podPassword bytes for data encryption from pod metadata + * @param feedType type of the feed * @param downloadOptions options for downloading */ export async function getFileMetadata( @@ -47,9 +48,10 @@ export async function getFileMetadata( path: string, address: EthAddress, podPassword: PodPasswordBytes, + feedType: FeedType, downloadOptions?: BeeRequestOptions, ): Promise { - const data = (await getRawMetadata(bee, path, address, podPassword, downloadOptions)).metadata + const data = (await getRawMetadata(bee, path, address, podPassword, feedType, downloadOptions)).metadata assertRawFileMetadata(data) return rawFileMetadataToFileMetadata(data) @@ -62,6 +64,7 @@ export async function getFileMetadata( * @param fullPath full path to the file * @param address address of the pod * @param podPassword bytes for data encryption from pod metadata + * @param feedType type of the feed * @param downloadOptions download options */ export async function downloadData( @@ -69,12 +72,12 @@ export async function downloadData( fullPath: string, address: EthAddress, podPassword: PodPasswordBytes, + feedType: FeedType, downloadOptions?: BeeRequestOptions, ): Promise { - const fileMetadata = await getFileMetadata(bee, fullPath, address, podPassword, downloadOptions) + const fileMetadata = await getFileMetadata(bee, fullPath, address, podPassword, feedType, downloadOptions) if (fileMetadata.compression) { - // TODO: implement compression support throw new Error('Compressed data is not supported yet') } @@ -132,6 +135,7 @@ export async function uploadData( connection.bee, fullPath, prepareEthAddress(podWallet.address), + connection.options?.feedType ?? FeedType.Epoch, connection.options?.requestOptions, ) const pathInfo = extractPathInfo(fullPath) @@ -165,15 +169,22 @@ export async function uploadData( mode: getFileMode(DEFAULT_FILE_PERMISSIONS), } - await addEntryToDirectory(connection, podWallet, pod.password, pathInfo.path, pathInfo.filename, true) - await writeFeedData( - connection, - fullPath, - getFileMetadataRawBytes(meta), - podWallet, - pod.password, - getNextEpoch(fullPathInfo?.lookupAnswer.epoch), - ) + const feedType = connection.options?.feedType ?? FeedType.Epoch + const writeFeedOptions: WriteFeedOptions = { + feedType, + } + + const epoch = fullPathInfo?.lookupAnswer.epoch + + if (feedType === FeedType.Epoch && epoch) { + writeFeedOptions.epochOptions = { + epoch: epoch, + isGetNextEpoch: true, + } + } + + await addEntryToDirectory(connection, podWallet, pod.password, pathInfo.path, pathInfo.filename, true, feedType) + await writeFeedData(connection, fullPath, getFileMetadataRawBytes(meta), podWallet, pod.password, writeFeedOptions) return meta } diff --git a/src/pod/api.ts b/src/pod/api.ts index b9e380ae..12cee7ef 100644 --- a/src/pod/api.ts +++ b/src/pod/api.ts @@ -7,17 +7,20 @@ import { utils } from 'ethers' import { DownloadOptions } from '../content-items/types' import { getWalletByIndex } from '../utils/cache/wallet' import { getPodsList as getPodsListCached } from './cache/api' +import { FeedType } from '../feed/types' /** * Gets pods list with lookup answer * * @param bee Bee instance * @param userWallet root wallet for downloading and decrypting data + * @param feedType feed type * @param downloadOptions request download */ export async function getPodsList( bee: Bee, userWallet: utils.HDNode, + feedType: FeedType, downloadOptions?: DownloadOptions, ): Promise { let lookupAnswer @@ -26,6 +29,7 @@ export async function getPodsList( bee, POD_TOPIC, prepareEthAddress(userWallet.address), + feedType, downloadOptions?.requestOptions, ) // eslint-disable-next-line no-empty @@ -50,6 +54,7 @@ export async function getPodsList( * @param podName pod to find * @param userWallet root wallet for downloading and decrypting data * @param seed seed of wallet owns the FDP account + * @param feedType feed type * @param downloadOptions request options */ export async function getExtendedPodsList( @@ -57,9 +62,10 @@ export async function getExtendedPodsList( podName: string, userWallet: utils.HDNode, seed: Uint8Array, + feedType: FeedType, downloadOptions?: DownloadOptions, ): Promise { - const { podsList, epoch } = await getPodsListCached(bee, userWallet, downloadOptions) + const { podsList, epoch } = await getPodsListCached(bee, userWallet, feedType, downloadOptions) const pod = podsList.pods.find(item => item.name === podName) if (!pod) { diff --git a/src/pod/cache/api.ts b/src/pod/cache/api.ts index a136d1c9..94d443ae 100644 --- a/src/pod/cache/api.ts +++ b/src/pod/cache/api.ts @@ -5,23 +5,26 @@ import { jsonToPodsList, podListToJSON, PodsInfo } from '../utils' import { CacheEpochData } from '../../cache/types' import { getCacheKey, processCacheData } from '../../cache/utils' import { getPodsList as getPodsNoCache } from '../api' +import { FeedType } from '../../feed/types' /** * Gets pods list with lookup answer * * @param bee Bee instance * @param userWallet root wallet for downloading and decrypting data + * @param feedType feed type * @param downloadOptions request download */ export async function getPodsList( bee: Bee, userWallet: utils.HDNode, + feedType: FeedType, downloadOptions?: DownloadOptions, ): Promise { return processCacheData({ key: getCacheKey(userWallet.address), onGetData: async (): Promise => { - const data = await getPodsNoCache(bee, userWallet, downloadOptions) + const data = await getPodsNoCache(bee, userWallet, feedType, downloadOptions) return { epoch: data.epoch, diff --git a/src/pod/personal-storage.ts b/src/pod/personal-storage.ts index 11e9fad8..5d6dad29 100644 --- a/src/pod/personal-storage.ts +++ b/src/pod/personal-storage.ts @@ -1,4 +1,4 @@ -import { SharedPod, PodReceiveOptions, PodShareInfo, PodsList, Pod, PodsListPrepared } from './types' +import { Pod, PodReceiveOptions, PodShareInfo, PodsList, PodsListPrepared, SharedPod } from './types' import { assertAccount } from '../account/utils' import { writeFeedData } from '../feed/api' import { AccountData } from '../account/account-data' @@ -11,10 +11,10 @@ import { createPodShareInfo, getSharedPodInfo, podListToBytes, - podsListPreparedToPodsList, + podListToJSON, podPreparedToPod, + podsListPreparedToPodsList, sharedPodPreparedToSharedPod, - podListToJSON, } from './utils' import { getExtendedPodsList } from './api' import { uploadBytes } from '../file/utils' @@ -24,7 +24,7 @@ import { assertEncryptedReference, EncryptedReference } from '../utils/hex' import { prepareEthAddress, preparePrivateKey } from '../utils/wallet' import { getCacheKey, setEpochCache } from '../cache/utils' import { getPodsList } from './cache/api' -import { getNextEpoch } from '../feed/lookup/utils' +import { FeedType, WriteFeedOptions } from '../feed/types' export const POD_TOPIC = 'Pods' @@ -46,10 +46,15 @@ export class PersonalStorage { try { podsList = ( - await getPodsList(this.accountData.connection.bee, this.accountData.wallet!, { - requestOptions: this.accountData.connection.options?.requestOptions, - cacheInfo: this.accountData.connection.cacheInfo, - }) + await getPodsList( + this.accountData.connection.bee, + this.accountData.wallet!, + this.accountData.connection.options?.feedType ?? FeedType.Epoch, + { + requestOptions: this.accountData.connection.options?.requestOptions, + cacheInfo: this.accountData.connection.cacheInfo, + }, + ) ).podsList // eslint-disable-next-line no-empty } catch (e) {} @@ -88,10 +93,15 @@ export class PersonalStorage { async delete(name: string): Promise { assertAccount(this.accountData) name = name.trim() - const podsInfo = await getPodsList(this.accountData.connection.bee, this.accountData.wallet!, { - requestOptions: this.accountData.connection.options?.requestOptions, - cacheInfo: this.accountData.connection.cacheInfo, - }) + const podsInfo = await getPodsList( + this.accountData.connection.bee, + this.accountData.wallet!, + this.accountData.connection.options?.feedType ?? FeedType.Epoch, + { + requestOptions: this.accountData.connection.options?.requestOptions, + cacheInfo: this.accountData.connection.cacheInfo, + }, + ) assertPodsLength(podsInfo.podsList.pods.length) const pod = podsInfo.podsList.pods.find(item => item.name === name) @@ -104,14 +114,27 @@ export class PersonalStorage { const podsSharedFiltered = podsInfo.podsList.sharedPods.filter(item => item.name !== name) const allPodsData = podListToBytes(podsFiltered, podsSharedFiltered) const wallet = this.accountData.wallet! - const epoch = getNextEpoch(podsInfo.epoch) + + const writeFeedOptions: WriteFeedOptions = { + feedType: this.accountData.connection.options?.feedType ?? FeedType.Epoch, + } + + const epoch = podsInfo.epoch + + if (writeFeedOptions.feedType === FeedType.Epoch && epoch) { + writeFeedOptions.epochOptions = { + epoch, + isGetNextEpoch: true, + } + } + await writeFeedData( this.accountData.connection, POD_TOPIC, allPodsData, wallet, preparePrivateKey(wallet.privateKey), - epoch, + writeFeedOptions, ) await setEpochCache(this.accountData.connection.cacheInfo, getCacheKey(wallet.address), { epoch, @@ -131,10 +154,17 @@ export class PersonalStorage { assertPodName(name) const wallet = this.accountData.wallet! const address = prepareEthAddress(wallet.address) - const podInfo = await getExtendedPodsList(this.accountData.connection.bee, name, wallet, this.accountData.seed!, { - requestOptions: this.accountData.connection.options?.requestOptions, - cacheInfo: this.accountData.connection.cacheInfo, - }) + const podInfo = await getExtendedPodsList( + this.accountData.connection.bee, + name, + wallet, + this.accountData.seed!, + this.accountData.connection.options?.feedType ?? FeedType.Epoch, + { + requestOptions: this.accountData.connection.options?.requestOptions, + cacheInfo: this.accountData.connection.cacheInfo, + }, + ) const data = stringToBytes( JSON.stringify(createPodShareInfo(name, podInfo.podAddress, address, podInfo.pod.password)), diff --git a/src/pod/utils.ts b/src/pod/utils.ts index d1dd8370..b17e7b8e 100644 --- a/src/pod/utils.ts +++ b/src/pod/utils.ts @@ -26,7 +26,7 @@ import { } from '../utils/type' import { bytesToHex, EncryptedReference, isHexEthAddress } from '../utils/hex' import { getExtendedPodsList } from './api' -import { Epoch, getFirstEpoch } from '../feed/lookup/epoch' +import { Epoch, getFirstEpoch } from '../feed/epoch' import { getUnixTimestamp } from '../utils/time' import { writeFeedData } from '../feed/api' import { preparePrivateKey } from '../utils/wallet' @@ -41,6 +41,7 @@ import { DEFAULT_DIRECTORY_PERMISSIONS, getDirectoryMode } from '../directory/ut import { getCacheKey, setEpochCache } from '../cache/utils' import { getWalletByIndex } from '../utils/cache/wallet' import { getPodsList } from './cache/api' +import { FeedType, WriteFeedOptions } from '../feed/types' export const META_VERSION = 2 export const MAX_PODS_COUNT = 65536 @@ -50,18 +51,40 @@ export const MAX_POD_NAME_LENGTH = 64 * Information about pods list */ export interface PodsInfo { + /** + * Pods list + */ podsList: PodsListPrepared - epoch: Epoch + + /** + * Epoch from the epoch feed + */ + epoch?: Epoch } /** * Extended information about specific pod */ export interface ExtendedPodInfo { + /** + * Pod + */ pod: PodPrepared + + /** + * Pod wallet + */ podWallet: utils.HDNode + + /** + * Pod address + */ podAddress: Utils.EthAddress - epoch: Epoch + + /** + * Epoch from the epoch feed + */ + epoch?: Epoch } /* @@ -369,11 +392,12 @@ export async function createPod( pod.name = pod.name.trim() assertPodName(pod.name) + const feedType = connection.options?.feedType ?? FeedType.Epoch const { cacheInfo } = connection let podsList: PodsListPrepared = { pods: [], sharedPods: [] } let podsInfo try { - podsInfo = await getPodsList(bee, userWallet, { + podsInfo = await getPodsList(bee, userWallet, feedType, { requestOptions: connection.options?.requestOptions, cacheInfo, }) @@ -388,7 +412,18 @@ export async function createPod( assertSharedPodNameAvailable(pod.name, sharedPods) const currentTime = getUnixTimestamp() - const epoch = podsInfo ? podsInfo.epoch.getNextEpoch(currentTime) : getFirstEpoch(currentTime) + let epoch + const writeFeedOptions: WriteFeedOptions = { + feedType, + } + + if (writeFeedOptions.feedType === FeedType.Epoch) { + epoch = podsInfo && podsInfo.epoch ? podsInfo.epoch.getNextEpoch(currentTime) : getFirstEpoch(currentTime) + writeFeedOptions.epochOptions = { + epoch, + } + } + let realPod: PodPrepared | SharedPodPrepared if (isSharedPod(pod)) { @@ -404,11 +439,21 @@ export async function createPod( } const allPodsData = podListToBytes(pods, sharedPods) - await writeFeedData(connection, POD_TOPIC, allPodsData, userWallet, preparePrivateKey(userWallet.privateKey), epoch) + await writeFeedData( + connection, + POD_TOPIC, + allPodsData, + userWallet, + preparePrivateKey(userWallet.privateKey), + writeFeedOptions, + ) if (isPod(realPod)) { const podWallet = await getWalletByIndex(seed, nextIndex, cacheInfo) - await createRootDirectory(connection, realPod.password, podWallet) + const writeFeedOptions: WriteFeedOptions = { + feedType, + } + await createRootDirectory(connection, realPod.password, podWallet, writeFeedOptions) } await setEpochCache(cacheInfo, getCacheKey(userWallet.address), { @@ -429,10 +474,17 @@ export async function getExtendedPodsListByAccountData( accountData: AccountData, podName: string, ): Promise { - return getExtendedPodsList(accountData.connection.bee, podName, accountData.wallet!, accountData.seed!, { - requestOptions: accountData.connection.options?.requestOptions, - cacheInfo: accountData.connection.cacheInfo, - }) + return getExtendedPodsList( + accountData.connection.bee, + podName, + accountData.wallet!, + accountData.seed!, + accountData.connection.options?.feedType ?? FeedType.Epoch, + { + requestOptions: accountData.connection.options?.requestOptions, + cacheInfo: accountData.connection.cacheInfo, + }, + ) } /** diff --git a/src/types.ts b/src/types.ts index e84634e0..1e81195b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,7 @@ import { BeeRequestOptions } from '@ethersphere/bee-js' import { EnsEnvironment } from '@fairdatasociety/fdp-contracts-js' import { CacheOptions } from './cache/types' +import { FeedType } from './feed/types' export { DirectoryItem, FileItem } from './content-items/types' @@ -24,4 +25,8 @@ export interface Options { * Cache options */ cacheOptions?: CacheOptions + /** + * Feed type + */ + feedType?: FeedType } diff --git a/src/utils/hex.ts b/src/utils/hex.ts index 85dfa387..da89cac0 100644 --- a/src/utils/hex.ts +++ b/src/utils/hex.ts @@ -6,7 +6,6 @@ export type HexEthAddress = HexString<40> /** * Nominal type to represent hex strings WITHOUT '0x' prefix. * For example for 32 bytes hex representation you have to use 64 length. - * TODO: Make Length mandatory: https://github.com/ethersphere/bee-js/issues/208 */ export type HexString = FlavoredType< string & { diff --git a/test/integration/fdp-class.fairos.spec.ts b/test/integration/fdp-class.fairos.spec.ts index 2f951f88..74be8b53 100644 --- a/test/integration/fdp-class.fairos.spec.ts +++ b/test/integration/fdp-class.fairos.spec.ts @@ -1,12 +1,13 @@ import { createFdp, generateRandomHexString, generateUser, topUpAddress, topUpFdp, waitFairOS } from '../utils' import { Directories, FairOSApi, PodsList } from '../utils/fairos-api' -import { Wallet, utils } from 'ethers' +import { utils, Wallet } from 'ethers' import { wrapBytesWithHelpers } from '../../src/utils/bytes' import { getExtendedPodsListByAccountData } from '../../src/pod/utils' import { getRawMetadata } from '../../src/content-items/utils' import { RawDirectoryMetadata, RawFileMetadata } from '../../src/pod/types' import { DEFAULT_FILE_PERMISSIONS, getFileMode } from '../../src/file/utils' import { DEFAULT_DIRECTORY_PERMISSIONS, getDirectoryMode } from '../../src/directory/utils' +import { FeedType } from '../../src/feed/types' jest.setTimeout(400000) describe('Fair Data Protocol with FairOS-dfs', () => { @@ -540,23 +541,24 @@ describe('Fair Data Protocol with FairOS-dfs', () => { const { podAddress, pod } = await getExtendedPodsListByAccountData(fdp.account, podName1) const rawDirectoryMetadata = ( - await getRawMetadata(fdp.connection.bee, fullNewDirectory1, podAddress, pod.password) + await getRawMetadata(fdp.connection.bee, fullNewDirectory1, podAddress, pod.password, FeedType.Epoch) ).metadata as RawDirectoryMetadata checkDirectoryMetadata(rawDirectoryMetadata, newDirectory1) - const rawFileMetadata = (await getRawMetadata(fdp.connection.bee, fullFilenameBigPath, podAddress, pod.password)) - .metadata as RawFileMetadata + const rawFileMetadata = ( + await getRawMetadata(fdp.connection.bee, fullFilenameBigPath, podAddress, pod.password, FeedType.Epoch) + ).metadata as RawFileMetadata checkFileMetadata(rawFileMetadata, filenameBig, fileSizeBig, 'text/plain; charset=utf-8') await fdp.directory.create(podName1, fullNewDirectory2) await fdp.file.uploadData(podName1, fullFilenameBigPath2, contentBig2) const rawDirectoryMetadata1 = ( - await getRawMetadata(fdp.connection.bee, fullNewDirectory2, podAddress, pod.password) + await getRawMetadata(fdp.connection.bee, fullNewDirectory2, podAddress, pod.password, FeedType.Epoch) ).metadata as RawDirectoryMetadata checkDirectoryMetadata(rawDirectoryMetadata1, newDirectory2) const rawFileMetadata1 = ( - await getRawMetadata(fdp.connection.bee, fullFilenameBigPath2, podAddress, pod.password) + await getRawMetadata(fdp.connection.bee, fullFilenameBigPath2, podAddress, pod.password, FeedType.Epoch) ).metadata as RawFileMetadata checkFileMetadata(rawFileMetadata1, filenameBig2, fileSizeBig, '') }) diff --git a/test/integration/fdp-class.spec.ts b/test/integration/fdp-class.spec.ts index f1765ff3..18c16c6e 100644 --- a/test/integration/fdp-class.spec.ts +++ b/test/integration/fdp-class.spec.ts @@ -12,22 +12,23 @@ import { MAX_POD_NAME_LENGTH } from '../../src/pod/utils' import { createUserV1 } from '../../src/account/account' import { PodShareInfo, RawFileMetadata } from '../../src/pod/types' import { FileShareInfo } from '../../src/file/types' -import { getFeedData } from '../../src/feed/api' import * as feedApi from '../../src/feed/api' +import { getFeedData } from '../../src/feed/api' import { POD_TOPIC } from '../../src/pod/personal-storage' import { decryptBytes } from '../../src/utils/encryption' import { Wallet } from 'ethers' import { removeZeroFromHex } from '../../src/account/utils' import { bytesToString, wrapBytesWithHelpers } from '../../src/utils/bytes' +import * as walletApi from '../../src/utils/wallet' import { mnemonicToSeed, prepareEthAddress } from '../../src/utils/wallet' import { assertEncryptedReference } from '../../src/utils/hex' import { base64toReference } from '../../src/file/utils' import path from 'path' import { getNodeFileContent } from '../../src/directory/utils' import { ETH_ADDR_HEX_LENGTH } from '../../src/utils/type' -import * as walletApi from '../../src/utils/wallet' -import { HIGHEST_LEVEL } from '../../src/feed/lookup/epoch' +import { HIGHEST_LEVEL } from '../../src/feed/epoch' import { getWalletByIndex } from '../../src/utils/cache/wallet' +import { FeedType } from '../../src/feed/types' jest.setTimeout(400000) describe('Fair Data Protocol class', () => { @@ -741,7 +742,7 @@ describe('Fair Data Protocol class', () => { // check pod metadata const pod1 = await fdp.personalStorage.create(pod) - const podData = await getFeedData(bee, POD_TOPIC, prepareEthAddress(user.address)) + const podData = await getFeedData(bee, POD_TOPIC, prepareEthAddress(user.address), FeedType.Epoch) const encryptedText1 = podData.data.chunkContent().text() const encryptedBytes1 = podData.data.chunkContent() // data decrypts with wallet for the pod. Data inside the pod will be encrypted with a password stored in the pod @@ -750,7 +751,7 @@ describe('Fair Data Protocol class', () => { expect(decryptedText1).toContain(pod) // HDNode with index 1 is for first pod const node1 = await getWalletByIndex(seed, 1) - const rootDirectoryData = await getFeedData(bee, '/', prepareEthAddress(node1.address)) + const rootDirectoryData = await getFeedData(bee, '/', prepareEthAddress(node1.address), FeedType.Epoch) const encryptedText2 = rootDirectoryData.data.chunkContent().text() const encryptedBytes2 = rootDirectoryData.data.chunkContent() // data decrypts with password stored in the pod @@ -764,7 +765,7 @@ describe('Fair Data Protocol class', () => { // check directory metadata await fdp.directory.create(pod, fullDirectory) - const fullDirectoryData = await getFeedData(bee, fullDirectory, prepareEthAddress(node1.address)) + const fullDirectoryData = await getFeedData(bee, fullDirectory, prepareEthAddress(node1.address), FeedType.Epoch) const encryptedText3 = fullDirectoryData.data.chunkContent().text() const encryptedBytes3 = fullDirectoryData.data.chunkContent() const decryptedText3 = bytesToString(decryptBytes(pod1.password, encryptedBytes3)) @@ -775,7 +776,12 @@ describe('Fair Data Protocol class', () => { } await fdp.file.uploadData(pod, fullFilenameSmallPath, contentSmall) - const fileManifestData = await getFeedData(bee, fullFilenameSmallPath, prepareEthAddress(node1.address)) + const fileManifestData = await getFeedData( + bee, + fullFilenameSmallPath, + prepareEthAddress(node1.address), + FeedType.Epoch, + ) const encryptedText4 = fileManifestData.data.chunkContent().text() const encryptedBytes4 = fileManifestData.data.chunkContent() const decryptedText4 = bytesToString(decryptBytes(pod1.password, encryptedBytes4)) @@ -826,7 +832,7 @@ describe('Fair Data Protocol class', () => { // no cache - create first pod await fdpNoCache.personalStorage.create(pod1) // for the first feed write it should be the highest level - expect(writeFeedDataSpy.mock.calls[0][5]?.level).toEqual(HIGHEST_LEVEL) + expect(writeFeedDataSpy.mock.calls[0][5]?.epochOptions?.epoch?.level).toEqual(HIGHEST_LEVEL) // getting info about pods from the network expect(getFeedDataSpy).toBeCalledTimes(1) // calculating wallet by index for the pod @@ -834,7 +840,7 @@ describe('Fair Data Protocol class', () => { jest.clearAllMocks() await fdpNoCache.personalStorage.create(pod2) - expect(writeFeedDataSpy.mock.calls[0][5]?.level).toEqual(HIGHEST_LEVEL - 1) + expect(writeFeedDataSpy.mock.calls[0][5]?.epochOptions?.epoch?.level).toEqual(HIGHEST_LEVEL - 1) expect(getFeedDataSpy).toBeCalledTimes(1) expect(getWalletByIndexSpy).toBeCalledTimes(1) @@ -904,7 +910,7 @@ describe('Fair Data Protocol class', () => { // with cache - create first pod await fdpWithCache.personalStorage.create(pod1) // for the first feed write it should be the highest level - expect(writeFeedDataSpy.mock.calls[0][5]?.level).toEqual(HIGHEST_LEVEL) + expect(writeFeedDataSpy.mock.calls[0][5]?.epochOptions?.epoch?.level).toEqual(HIGHEST_LEVEL) // getting info about pods from the network expect(getFeedDataSpy).toBeCalledTimes(1) // calculating wallet by index for the pod @@ -915,7 +921,7 @@ describe('Fair Data Protocol class', () => { // data about pods shouldn't be retrieved expect(getFeedDataSpy).toBeCalledTimes(0) // should be calculated correct level without getting previous one from the network - expect(writeFeedDataSpy.mock.calls[0][5]?.level).toEqual(HIGHEST_LEVEL - 1) + expect(writeFeedDataSpy.mock.calls[0][5]?.epochOptions?.epoch?.level).toEqual(HIGHEST_LEVEL - 1) // calc a wallet for the new pod expect(getWalletByIndexSpy).toBeCalledTimes(1) diff --git a/test/unit/feed/epoch.spec.ts b/test/unit/feed/epoch.spec.ts index d1a40abf..d8d1bf71 100644 --- a/test/unit/feed/epoch.spec.ts +++ b/test/unit/feed/epoch.spec.ts @@ -1,4 +1,4 @@ -import { Epoch, getBaseTime } from '../../../src/feed/lookup/epoch' +import { Epoch, getBaseTime } from '../../../src/feed/epoch' describe('feed/epoch', () => { it('getBaseTime', () => { diff --git a/test/unit/feed/sequential.spec.ts b/test/unit/feed/sequential.spec.ts new file mode 100644 index 00000000..3826a430 --- /dev/null +++ b/test/unit/feed/sequential.spec.ts @@ -0,0 +1,23 @@ +import { Bee, Topic } from '@ethersphere/bee-js' +import { batchId, beeUrl } from '../../utils' +import { Wallet } from 'ethers' +import { bytesToString } from '../../../src/utils/bytes' + +describe('feed/sequential', () => { + it('check', async () => { + // const wallet = Wallet.createRandom() + // const bee = new Bee(beeUrl()) + // const data1 = 'Hello world 1' + // const data2 = 'Hello world 2' + // // const topic = Utils.makeHexString('hello') + // const topic = '0000000000000000000000000000000000000000000000000000000000000000' as Topic + // const referenceData1 = await bee.uploadData(batchId(), data1) + // const referenceData2 = await bee.uploadData(batchId(), data2) + // const writer = bee.makeFeedWriter('sequence', topic, wallet.privateKey) + // const reader = bee.makeFeedReader('sequence', topic, wallet.address) + // await writer.upload(batchId(), referenceData1.reference) + // expect(bytesToString(await bee.downloadData((await reader.download()).reference))).toEqual(data1) + // await writer.upload(batchId(), referenceData2.reference) + // expect(bytesToString(await bee.downloadData((await reader.download()).reference))).toEqual(data2) + }) +}) diff --git a/test/unit/handler.spec.ts b/test/unit/handler.spec.ts index 284da8a9..1080fef0 100644 --- a/test/unit/handler.spec.ts +++ b/test/unit/handler.spec.ts @@ -1,5 +1,5 @@ import { getId } from '../../src/feed/handler' -import { Epoch } from '../../src/feed/lookup/epoch' +import { Epoch } from '../../src/feed/epoch' import { numbersToSegment } from '../utils' describe('handler', () => { From dc223869595f86950e9f843f1deafcdafb72cea5 Mon Sep 17 00:00:00 2001 From: Igor Shadurin Date: Thu, 27 Jul 2023 16:35:26 +0300 Subject: [PATCH 2/3] fix: feed speed check --- src/content-items/handler.ts | 19 ++++--- src/directory/handler.ts | 3 +- src/feed/api.ts | 5 +- src/feed/sequence.ts | 34 ++++++++++-- src/feed/types.ts | 63 ++++++++++++++++++++++ src/pod/cache/api.ts | 2 +- test/integration/feeds-speed-check.spec.ts | 48 +++++++++++++++++ test/unit/feed/sequential.spec.ts | 23 -------- test/utils.ts | 10 +++- 9 files changed, 167 insertions(+), 40 deletions(-) create mode 100644 test/integration/feeds-speed-check.spec.ts delete mode 100644 test/unit/feed/sequential.spec.ts diff --git a/src/content-items/handler.ts b/src/content-items/handler.ts index 7bde2cae..a94bfa61 100644 --- a/src/content-items/handler.ts +++ b/src/content-items/handler.ts @@ -80,13 +80,20 @@ export async function addEntryToDirectory( parentData.fileOrDirNames.push(itemToAdd) parentData.meta.modificationTime = getUnixTimestamp() - return writeFeedData(connection, parentPath, getRawDirectoryMetadataBytes(parentData), wallet, podPassword, { + const epoch = metadataWithEpoch.epoch + const writeFeedOptions: WriteFeedOptions = { feedType, - epochOptions: { - epoch: metadataWithEpoch.epoch, - isGetNextEpoch: true, - }, - }) + ...(feedType === FeedType.Epoch && epoch ? { epochOptions: { epoch, isGetNextEpoch: true } } : {}), + } + + return writeFeedData( + connection, + parentPath, + getRawDirectoryMetadataBytes(parentData), + wallet, + podPassword, + writeFeedOptions, + ) } /** diff --git a/src/directory/handler.ts b/src/directory/handler.ts index 48097252..ce2ac290 100644 --- a/src/directory/handler.ts +++ b/src/directory/handler.ts @@ -27,7 +27,6 @@ import { PodPasswordBytes } from '../utils/encryption' import { DataUploadOptions } from '../file/types' import { DirectoryItem } from '../content-items/types' import { prepareEthAddress } from '../utils/wallet' -import { getNextEpoch } from '../feed/lookup/utils' import { FeedType, WriteFeedOptions } from '../feed/types' /** @@ -185,7 +184,7 @@ export async function createDirectory( const epoch = pathInfo?.lookupAnswer.epoch const writeFeedOptions: WriteFeedOptions = { feedType, - ...(feedType === FeedType.Epoch && epoch ? { epochOptions: { epoch: getNextEpoch(epoch) } } : {}), + ...(feedType === FeedType.Epoch && epoch ? { epochOptions: { epoch, isGetNextEpoch: true } } : {}), } await createDirectoryInfo(connection, parentPath, name, podPassword, podWallet, writeFeedOptions) } diff --git a/src/feed/api.ts b/src/feed/api.ts index 1f403abd..713091cd 100644 --- a/src/feed/api.ts +++ b/src/feed/api.ts @@ -5,7 +5,7 @@ import { assertWriteFeedOptions, FeedType, LookupAnswer, WriteFeedOptions } from import { Connection } from '../connection/connection' import { encryptBytes, PodPasswordBytes } from '../utils/encryption' import { utils, Wallet } from 'ethers' -import { writeSequenceFeedData } from './sequence' +import { getSequenceFeedData, writeSequenceFeedData } from './sequence' import { lookupWithEpoch } from './lookup/linear' import { getNextEpoch } from './lookup/utils' @@ -35,8 +35,7 @@ export async function getFeedData( if (feedType === FeedType.Epoch) { return lookupWithEpoch(bee, topicHash, address, requestOptions) } else if (feedType === FeedType.Sequence) { - // todo implement with sequence feed reader - throw new Error('Sequence feed not implemented yet') + return getSequenceFeedData(bee, topicHash, address, requestOptions) } else { throw new Error('Unknown feed type') } diff --git a/src/feed/sequence.ts b/src/feed/sequence.ts index 0de7eb1f..76c2664e 100644 --- a/src/feed/sequence.ts +++ b/src/feed/sequence.ts @@ -1,8 +1,8 @@ import { Connection } from '../connection/connection' import { utils, Wallet } from 'ethers' -import { Reference } from '@ethersphere/bee-js' +import { Bee, BeeRequestOptions, Data, Reference, Utils } from '@ethersphere/bee-js' import { bmtHashString } from '../account/utils' -import { FeedType } from './types' +import { FeedType, LookupAnswer, LookupData, LookupDataWithChunkContent } from './types' /** * Writes data to sequence feed @@ -20,7 +20,7 @@ export async function writeSequenceFeedData( ): Promise { const topicHash = bmtHashString(topic) const feedWriter = connection.bee.makeFeedWriter( - FeedType.Epoch, + FeedType.Sequence, topicHash, wallet.privateKey, connection.options?.requestOptions, @@ -30,3 +30,31 @@ export async function writeSequenceFeedData( return feedWriter.upload(connection.postageBatchId, dataReference.reference) } + +/** + * Reads data from sequence feed + * + * @param bee Bee instance + * @param topic topic to read from + * @param address owner of the feed + * @param requestOptions Bee request options + */ +export async function getSequenceFeedData( + bee: Bee, + topic: Utils.Bytes<32>, + address: Utils.EthAddress | Uint8Array, + requestOptions?: BeeRequestOptions, +): Promise { + const reader = bee.makeFeedReader(FeedType.Sequence, topic, address, requestOptions) + const downloaded = await reader.download() + const data: Data = await bee.downloadData(downloaded.reference, requestOptions) + + const lookupData: LookupData = new LookupDataWithChunkContent(data) + + return { + data: lookupData, + sequenceInfo: { + ...downloaded, + }, + } +} diff --git a/src/feed/types.ts b/src/feed/types.ts index 74b3f56b..559f2a94 100644 --- a/src/feed/types.ts +++ b/src/feed/types.ts @@ -1,5 +1,43 @@ import { Data } from '@ethersphere/bee-js' import { Epoch } from './epoch' +import { bytesToHex, HexString } from '../utils/hex' + +/** + * Lookup data with chunk content + */ +export class LookupDataWithChunkContent extends Uint8Array implements LookupData { + constructor(array: Uint8Array) { + super(array.buffer) + } + + /** + * Data as string + */ + text(): string { + return new TextDecoder('utf-8').decode(this) + } + + /** + * Data as hex string + */ + hex(): HexString { + return JSON.parse(new TextDecoder('utf-8').decode(this)) + } + + /** + * Data as JSON + */ + json(): any { + return bytesToHex(this) + } + + /** + * Chunk content + */ + chunkContent() { + return this + } +} /** * Lookup data with possibility to get chunk content @@ -8,6 +46,26 @@ export interface LookupData extends Data { chunkContent(): Data } +/** + * Sequence info + */ +export interface SequenceInfo { + /** + * Reference + */ + reference: string + + /** + * Feed index + */ + feedIndex: string + + /** + * Feed index next + */ + feedIndexNext: string +} + /** * Result information for lookup methods with extended information */ @@ -21,6 +79,11 @@ export interface LookupAnswer { * Epoch info only for epoch feed */ epoch?: Epoch + + /** + * Sequence info only for sequence feed + */ + sequenceInfo?: SequenceInfo } /** diff --git a/src/pod/cache/api.ts b/src/pod/cache/api.ts index 94d443ae..d2dd25ae 100644 --- a/src/pod/cache/api.ts +++ b/src/pod/cache/api.ts @@ -32,7 +32,7 @@ export async function getPodsList( } }, onRecoverData: async (data): Promise => { - if (!(data.data && data.epoch)) { + if (!data.data) { throw new Error('Incorrect recovered cache data for pods list') } diff --git a/test/integration/feeds-speed-check.spec.ts b/test/integration/feeds-speed-check.spec.ts new file mode 100644 index 00000000..71841ad3 --- /dev/null +++ b/test/integration/feeds-speed-check.spec.ts @@ -0,0 +1,48 @@ +import { batchId, beeUrl, fdpOptions, generateRandomHexString } from '../utils' +import { FdpStorage } from '../../src' +import { FeedType } from '../../src/feed/types' +import { BatchId } from '@ethersphere/bee-js' + +jest.setTimeout(400000) +describe('feed/sequential', () => { + it('check', async () => { + const beeNodeUrl = 'https://bee-1.fairdatasociety.org/' + const stamp = '0'.repeat(64) as BatchId + // const beeNodeUrl = beeUrl() + // const stamp = batchId() + const fdp = new FdpStorage(beeNodeUrl, stamp, { + ...fdpOptions, + requestOptions: { + timeout: 100000, + }, + feedType: FeedType.Sequence, + ...{ + cacheOptions: { + isUseCache: false, + }, + }, + }) + + const pod1 = generateRandomHexString() + const pod2 = generateRandomHexString() + const dir1 = `/${generateRandomHexString()}` + const dir2 = `/${generateRandomHexString()}` + const fileSize = 100000 + const fileContent = generateRandomHexString(fileSize) + const filename = generateRandomHexString() + '.txt' + const fullFilename = '/' + filename + fdp.account.createWallet() + await fdp.personalStorage.create(pod1) + await fdp.personalStorage.create(pod2) + await fdp.directory.create(pod1, dir1) + await fdp.directory.create(pod1, dir2) + await fdp.directory.create(pod2, dir1) + await fdp.directory.create(pod2, dir2) + await fdp.file.uploadData(pod1, fullFilename, fileContent) + await fdp.file.uploadData(pod2, fullFilename, fileContent) + await fdp.file.downloadData(pod1, fullFilename) + await fdp.file.downloadData(pod2, fullFilename) + await fdp.directory.read(pod1, '/', true) + await fdp.directory.read(pod2, '/', true) + }) +}) diff --git a/test/unit/feed/sequential.spec.ts b/test/unit/feed/sequential.spec.ts deleted file mode 100644 index 3826a430..00000000 --- a/test/unit/feed/sequential.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Bee, Topic } from '@ethersphere/bee-js' -import { batchId, beeUrl } from '../../utils' -import { Wallet } from 'ethers' -import { bytesToString } from '../../../src/utils/bytes' - -describe('feed/sequential', () => { - it('check', async () => { - // const wallet = Wallet.createRandom() - // const bee = new Bee(beeUrl()) - // const data1 = 'Hello world 1' - // const data2 = 'Hello world 2' - // // const topic = Utils.makeHexString('hello') - // const topic = '0000000000000000000000000000000000000000000000000000000000000000' as Topic - // const referenceData1 = await bee.uploadData(batchId(), data1) - // const referenceData2 = await bee.uploadData(batchId(), data2) - // const writer = bee.makeFeedWriter('sequence', topic, wallet.privateKey) - // const reader = bee.makeFeedReader('sequence', topic, wallet.address) - // await writer.upload(batchId(), referenceData1.reference) - // expect(bytesToString(await bee.downloadData((await reader.download()).reference))).toEqual(data1) - // await writer.upload(batchId(), referenceData2.reference) - // expect(bytesToString(await bee.downloadData((await reader.download()).reference))).toEqual(data2) - }) -}) diff --git a/test/utils.ts b/test/utils.ts index 34f3c5c8..26fa71ed 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -5,6 +5,7 @@ import { utils, Wallet } from 'ethers' import { Environments, getEnsEnvironmentConfig } from '@fairdatasociety/fdp-contracts-js' import axios from 'axios' import { CacheOptions } from '../src/cache/types' +import { FeedType } from '../src/feed/types' export interface TestUser { username: string @@ -109,13 +110,18 @@ export const fdpOptions: Options = { cacheOptions: { isUseCache: false, }, + feedType: FeedType.Epoch, } /** * Creates FDP instance with default configuration for testing */ -export function createFdp(cacheOptions?: CacheOptions): FdpStorage { - return new FdpStorage(beeUrl(), batchId(), { ...fdpOptions, ...(cacheOptions ? { cacheOptions } : undefined) }) +export function createFdp(cacheOptions?: CacheOptions, feedType = FeedType.Epoch): FdpStorage { + return new FdpStorage(beeUrl(), batchId(), { + ...fdpOptions, + ...{ feedType }, + ...(cacheOptions ? { cacheOptions } : undefined), + }) } /** From f54b64c99ff6de1dfd32c5cba5593acf26dba164 Mon Sep 17 00:00:00 2001 From: Igor Shadurin Date: Thu, 27 Jul 2023 16:48:44 +0300 Subject: [PATCH 3/3] fix: test renamed --- .../{speed-check.spec.ts => cache-speed-check.spec.ts} | 0 test/integration/feeds-speed-check.spec.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename test/integration/{speed-check.spec.ts => cache-speed-check.spec.ts} (100%) diff --git a/test/integration/speed-check.spec.ts b/test/integration/cache-speed-check.spec.ts similarity index 100% rename from test/integration/speed-check.spec.ts rename to test/integration/cache-speed-check.spec.ts diff --git a/test/integration/feeds-speed-check.spec.ts b/test/integration/feeds-speed-check.spec.ts index 71841ad3..5cbacdf4 100644 --- a/test/integration/feeds-speed-check.spec.ts +++ b/test/integration/feeds-speed-check.spec.ts @@ -4,7 +4,7 @@ import { FeedType } from '../../src/feed/types' import { BatchId } from '@ethersphere/bee-js' jest.setTimeout(400000) -describe('feed/sequential', () => { +describe('feeds speed check', () => { it('check', async () => { const beeNodeUrl = 'https://bee-1.fairdatasociety.org/' const stamp = '0'.repeat(64) as BatchId