Skip to content

Commit

Permalink
feat: clear postage batch error (#280)
Browse files Browse the repository at this point in the history
* feat: enhance account assertion with write access check

* refactor: clarify account and postage batch id requirements in method comments
  • Loading branch information
IgorShadurin authored Oct 25, 2023
1 parent b1f89a1 commit 776eb83
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 19 deletions.
18 changes: 15 additions & 3 deletions src/account/account-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ export class AccountData {
/**
* Exports wallet from version 1 account
*
* Account and postage batch id are not required
*
* @deprecated the method will be removed after an accounts' migration process is completed
*
* @param username username from version 1 account
Expand Down Expand Up @@ -109,6 +111,8 @@ export class AccountData {
/**
* Migrates from FDP account without ENS to account with ENS
*
* Account and postage batch id are not required
*
* @deprecated the method will be removed after an accounts' migration process is completed
*
* @param username username from version 1 account
Expand All @@ -128,6 +132,8 @@ export class AccountData {
/**
* Logs in with the FDP credentials and gives back ethers wallet
*
* Account and postage batch id are not required
*
* @param username FDP username
* @param password password of the wallet
*
Expand Down Expand Up @@ -160,6 +166,8 @@ export class AccountData {
* the existing RegistrationRequest object. The process will continue from the failed
* step.
*
* Account and postage batch id are not required
*
* @param username FDP username
* @param password FDP password
*
Expand All @@ -173,16 +181,18 @@ export class AccountData {
}

/**
* Creates new FDP account and gives back user account with swarm reference
* Creates a new FDP account and gives back user account with swarm reference
*
* Account and postage batch id are required
*
* @param request a RegistrationRequest object that contains the state of registration process
*/
async register(request: RegistrationRequest): Promise<Reference> {
const { username, password, ensCompleted } = request

assertAccount(this, { writeRequired: true })
assertUsername(username)
assertPassword(password)
assertAccount(this)

const wallet = this.wallet!

Expand Down Expand Up @@ -235,11 +245,13 @@ export class AccountData {
/**
* Re-uploads portable account without registration in ENS
*
* Account and postage batch id are required
*
* @param username FDP username
* @param password FDP password
*/
async reuploadPortableAccount(username: string, password: string): Promise<void> {
assertAccount(this)
assertAccount(this, { writeRequired: true })

const wallet = this.wallet!
const seed = CryptoJS.enc.Hex.parse(removeZeroFromHex(bytesToHex(this.seed!)))
Expand Down
10 changes: 10 additions & 0 deletions src/account/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,13 @@ export function isAddressOptions(options: unknown): options is AddressOptions {
export function isMnemonicOptions(options: unknown): options is MnemonicOptions {
return isObject(options) && isValidMnemonic((options as MnemonicOptions).mnemonic)
}

/**
* Assert account options
*/
export interface AssertAccountOptions {
/**
* Check if write access is required
*/
writeRequired?: boolean
}
10 changes: 8 additions & 2 deletions src/account/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { makeSpan, stringToBytes, wrapBytesWithHelpers } from '../utils/bytes'
import { AccountData } from './account-data'
import { isValidMnemonic } from 'ethers/lib/utils'
import CryptoJS from 'crypto-js'
import { replaceAll } from '../utils/string'
import { assertBatchId, replaceAll } from '../utils/string'
import { assertString } from '../utils/type'
import { AssertAccountOptions } from './types'

export const MNEMONIC_LENGTH = 12
export const MAX_CHUNK_LENGTH = 4096
Expand Down Expand Up @@ -131,8 +132,9 @@ export function assertMnemonic(value: unknown): asserts value is string {
* Asserts whether an account is defined
*
* @param value instance of AccountData to check
* @param options options to check
*/
export function assertAccount(value: unknown): asserts value is AccountData {
export function assertAccount(value: unknown, options?: AssertAccountOptions): asserts value is AccountData {
const data = value as AccountData

if (!data.wallet) {
Expand All @@ -146,6 +148,10 @@ export function assertAccount(value: unknown): asserts value is AccountData {
if (!data.publicKey) {
throw new Error('Public key is empty')
}

if (options?.writeRequired) {
assertBatchId(data.connection.postageBatchId)
}
}

/**
Expand Down
14 changes: 11 additions & 3 deletions src/directory/directory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export class Directory {
/**
* Get files and directories under the given path
*
* Account is required, postage batch id is not required
*
* @param podName pod for content search
* @param path path to start searching from
* @param isRecursive search with recursion or not
Expand All @@ -52,11 +54,13 @@ export class Directory {
/**
* Creates a directory
*
* Account and postage batch id are required
*
* @param podName pod where to create a directory
* @param fullPath path for a directory
*/
async create(podName: string, fullPath: string): Promise<void> {
assertAccount(this.accountData)
assertAccount(this.accountData, { writeRequired: true })
assertPodName(podName)
const { podWallet, pod } = await getExtendedPodsListByAccountData(this.accountData, podName)

Expand All @@ -72,11 +76,13 @@ export class Directory {
/**
* Deletes a directory
*
* Account and postage batch id are required
*
* @param podName pod where to delete a directory
* @param fullPath path for a directory
*/
async delete(podName: string, fullPath: string): Promise<void> {
assertAccount(this.accountData)
assertAccount(this.accountData, { writeRequired: true })
assertPodName(podName)
const pathInfo = extractPathInfo(fullPath)
const connection = this.accountData.connection
Expand All @@ -96,12 +102,14 @@ export class Directory {
/**
* Uploads a directory with files
*
* Account and postage batch id are required
*
* @param podName pod where to upload a directory
* @param filesSource files source. path for Node.js, `FileList` for browser
* @param options upload directory options
*/
async upload(podName: string, filesSource: string | FileList, options?: UploadDirectoryOptions): Promise<void> {
assertAccount(this.accountData)
assertAccount(this.accountData, { writeRequired: true })
assertPodName(podName)
const { podWallet, pod } = await getExtendedPodsListByAccountData(this.accountData, podName)
options = { ...DEFAULT_UPLOAD_DIRECTORY_OPTIONS, ...options }
Expand Down
29 changes: 25 additions & 4 deletions src/file/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { BeeRequestOptions, 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 { assertBatchId } from '../utils/string'

/**
* Files management class
Expand All @@ -37,6 +38,8 @@ export class File {
/**
* Downloads file content
*
* Account is required, postage batch id is not required
*
* @param podName pod where file is stored
* @param fullPath full path of the file
* @param options download options
Expand All @@ -58,6 +61,8 @@ export class File {
/**
* Uploads file content
*
* Account and postage batch id are required
*
* @param podName pod where file is stored
* @param fullPath full path of the file
* @param data file content or ExternalDataBlock[] indexed in ascending order
Expand All @@ -70,7 +75,7 @@ export class File {
options?: DataUploadOptions,
): Promise<FileMetadata> {
options = { ...DEFAULT_UPLOAD_OPTIONS, ...options }
assertAccount(this.accountData)
assertAccount(this.accountData, { writeRequired: true })
assertPodName(podName)

return uploadData(podName, fullPath, data, this.accountData, options)
Expand All @@ -79,11 +84,13 @@ export class File {
/**
* Deletes a file
*
* Account and postage batch id are required
*
* @param podName pod where file is located
* @param fullPath full path of the file
*/
async delete(podName: string, fullPath: string): Promise<void> {
assertAccount(this.accountData)
assertAccount(this.accountData, { writeRequired: true })
assertFullPathWithName(fullPath)
assertPodName(podName)
const pathInfo = extractPathInfo(fullPath)
Expand All @@ -101,11 +108,13 @@ export class File {
/**
* Shares file information
*
* Account and postage batch id are required
*
* @param podName pod where file is stored
* @param fullPath full path of the file
*/
async share(podName: string, fullPath: string): Promise<Reference> {
assertAccount(this.accountData)
assertAccount(this.accountData, { writeRequired: true })
assertFullPathWithName(fullPath)
assertPodName(podName)

Expand All @@ -121,12 +130,13 @@ export class File {
/**
* Gets shared file information
*
* Account and postage batch id are not required
*
* @param reference swarm reference with shared file information
*
* @returns shared file information
*/
async getSharedInfo(reference: string | EncryptedReference): Promise<FileShareInfo> {
assertAccount(this.accountData)
assertEncryptedReference(reference)

return getSharedFileInfo(this.accountData.connection.bee, reference)
Expand All @@ -135,6 +145,8 @@ export class File {
/**
* Saves shared file to a personal account
*
* Account and postage batch id are required
*
* @param podName pod where file is stored
* @param parentPath the path to the file to save
* @param reference swarm reference with shared file information
Expand All @@ -148,6 +160,7 @@ export class File {
reference: string | EncryptedReference,
options?: FileReceiveOptions,
): Promise<FileMetadata> {
assertAccount(this.accountData, { writeRequired: true })
assertPodName(podName)
const sharedInfo = await this.getSharedInfo(reference)
const connection = this.accountData.connection
Expand All @@ -165,10 +178,14 @@ export class File {
/**
* Uploads a data block without constructing a file metadata
*
* Account is not required, postage batch id is required
*
* @param block block data
* @param blockIndex block index
*/
async uploadDataBlock(block: Uint8Array, blockIndex: number): Promise<ExternalDataBlock> {
assertBatchId(this.accountData.connection.postageBatchId)

return {
...(await uploadDataBlock(this.accountData.connection, block)),
index: blockIndex,
Expand All @@ -178,6 +195,8 @@ export class File {
/**
* Downloads file metadata with blocks data
*
* Account is required, postage batch id is not required
*
* @param podName pod where file is stored
* @param fullPath full path of the file
* @param downloadOptions bee download options
Expand Down Expand Up @@ -206,6 +225,8 @@ export class File {
/**
* Downloads data block using file metadata
*
* No account or postage batch id is required
*
* @param meta file metadata
* @param blockIndex block index
* @param downloadOptions bee download options
Expand Down
20 changes: 16 additions & 4 deletions src/pod/personal-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ export class PersonalStorage {
/**
* Gets the list of pods for the active account
*
* Account is required, postage batch id is not required
*
* @returns list of pods
*/
async list(): Promise<PodsList> {
Expand All @@ -60,10 +62,12 @@ export class PersonalStorage {
/**
* Creates new pod with passed name
*
* Account and postage batch id are required
*
* @param name pod name
*/
async create(name: string): Promise<Pod> {
assertAccount(this.accountData)
assertAccount(this.accountData, { writeRequired: true })

const pod = await createPod(
this.accountData.connection.bee,
Expand All @@ -83,10 +87,12 @@ export class PersonalStorage {
/**
* Deletes pod with passed name
*
* Account and postage batch id are required
*
* @param name pod name
*/
async delete(name: string): Promise<void> {
assertAccount(this.accountData)
assertAccount(this.accountData, { writeRequired: true })
name = name.trim()
const podsInfo = await getPodsList(this.accountData.connection.bee, this.accountData.wallet!, {
requestOptions: this.accountData.connection.options?.requestOptions,
Expand Down Expand Up @@ -122,12 +128,14 @@ export class PersonalStorage {
/**
* Shares pod information
*
* Account and postage batch id are required
*
* @param name pod name
*
* @returns swarm reference of shared metadata about pod
*/
async share(name: string): Promise<Reference> {
assertAccount(this.accountData)
assertAccount(this.accountData, { writeRequired: true })
assertPodName(name)
const wallet = this.accountData.wallet!
const address = prepareEthAddress(wallet.address)
Expand All @@ -146,6 +154,8 @@ export class PersonalStorage {
/**
* Gets shared pod information
*
* Account and postage batch id are not required
*
* @param reference swarm reference with shared pod information
*
* @returns shared pod information
Expand All @@ -159,13 +169,15 @@ export class PersonalStorage {
/**
* Receive and save shared pod information to user's account
*
* Account and postage batch id are required
*
* @param reference swarm reference with shared pod information
* @param options options for receiving pod
*
* @returns shared pod information
*/
async saveShared(reference: string | EncryptedReference, options?: PodReceiveOptions): Promise<SharedPod> {
assertAccount(this.accountData)
assertAccount(this.accountData, { writeRequired: true })
assertEncryptedReference(reference)

const data = await this.getSharedInfo(reference)
Expand Down
Loading

0 comments on commit 776eb83

Please sign in to comment.