diff --git a/src/bee-debug.ts b/src/bee-debug.ts index 6ebd4c18..879b215b 100644 --- a/src/bee-debug.ts +++ b/src/bee-debug.ts @@ -34,14 +34,14 @@ import type { NodeInfo, BeeVersions, } from './types' -import { BeeArgumentError } from './utils/error' +import { BeeArgumentError, BeeError } from './utils/error' import { assertBeeUrl, stripLastSlash } from './utils/url' import { assertAddress, assertBatchId, - assertBoolean, assertCashoutOptions, assertNonNegativeInteger, + assertPostageBatchOptions, assertRequestOptions, assertTransactionHash, isTag, @@ -60,6 +60,7 @@ import * as tag from './modules/debug/tag' import * as stamps from './modules/debug/stamps' import type { Options as KyOptions } from 'ky-universal' import { makeDefaultKy, wrapRequestClosure, wrapResponseClosure } from './utils/http' +import { sleep } from '../test/utils' export class BeeDebug { /** @@ -499,7 +500,7 @@ export class BeeDebug { * @see [Bee Debug API reference - `POST /stamps`](https://docs.ethswarm.org/debug-api/#tag/Postage-Stamps/paths/~1stamps~1{amount}~1{depth}/post) */ async createPostageBatch(amount: NumberString, depth: number, options?: PostageBatchOptions): Promise { - assertRequestOptions(options) + assertPostageBatchOptions(options) assertNonNegativeInteger(amount) assertNonNegativeInteger(depth) @@ -511,15 +512,13 @@ export class BeeDebug { throw new BeeArgumentError(`Depth has to be at most ${STAMPS_DEPTH_MAX}`, depth) } - if (options?.gasPrice) { - assertNonNegativeInteger(options.gasPrice) - } + const stamp = await stamps.createPostageBatch(this.getKy(options), amount, depth, options) - if (options?.immutableFlag !== undefined) { - assertBoolean(options.immutableFlag) + if (options?.waitForUsable) { + await this.waitForUsablePostageStamp(stamp, options?.waitForUsableTimeout) } - return stamps.createPostageBatch(this.getKy(options), amount, depth, options) + return stamp } /** @@ -673,6 +672,21 @@ export class BeeDebug { return transactions.cancelTransaction(this.getKy(options), transactionHash, gasPrice) } + private async waitForUsablePostageStamp(id: BatchId, timeout = 120_000): Promise { + const TIME_STEP = 1500 + for (let time = 0; time < timeout; time += TIME_STEP) { + const stamp = await this.getPostageBatch(id) + + if (stamp.usable) { + return + } + + await sleep(TIME_STEP) + } + + throw new BeeError('Timeout on waiting for postage stamp to become usable') + } + private getKy(options?: RequestOptions): Ky { if (!options) { return this.ky diff --git a/src/types/index.ts b/src/types/index.ts index 46fd41c7..1a233932 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -537,6 +537,24 @@ export interface PostageBatchOptions extends RequestOptions { */ gasPrice?: NumberString immutableFlag?: boolean + + /** + * The returned Promise will await until the purchased Postage Batch is usable. + * In other word, it has to have enough block confirmations that Bee pronounce it usable. + * If turned on, this significantly prolong the creation of postage batch! + * If you plan to use the stamp right away for some action with Bee (like uploading using this stamp) it is + * highly recommended to use this option, otherwise you might get errors "stamp not usable" from Bee. + * + * In next breaking release this option will be turned on by default. + * @default false + */ + waitForUsable?: boolean + + /** + * When waiting for the postage stamp to become usable, this specify the timeout for the waiting. + * Default: 120s + */ + waitForUsableTimeout?: number } /** diff --git a/src/utils/type.ts b/src/utils/type.ts index 7269ffcc..57f18c58 100644 --- a/src/utils/type.ts +++ b/src/utils/type.ts @@ -322,6 +322,14 @@ export function assertPostageBatchOptions(value: unknown): asserts value is Post if (options?.immutableFlag !== undefined) { assertBoolean(options.immutableFlag) } + + if (options?.waitForUsable !== undefined) { + assertBoolean(options.waitForUsable) + } + + if (options?.waitForUsableTimeout !== undefined) { + assertNonNegativeInteger(options.waitForUsableTimeout, 'options.waitForUsableTimeout') + } } export function assertCashoutOptions(value: unknown): asserts value is CashoutOptions { diff --git a/test/integration/bee-debug-class.spec.ts b/test/integration/bee-debug-class.spec.ts index 6cdf6454..82cda9f4 100644 --- a/test/integration/bee-debug-class.spec.ts +++ b/test/integration/bee-debug-class.spec.ts @@ -1,5 +1,12 @@ import { BeeArgumentError, BeeDebug } from '../../src' -import { beeDebugUrl, commonMatchers, getOrCreatePostageBatch, BLOCKCHAIN_TRANSACTION_TIMEOUT, sleep } from '../utils' +import { + beeDebugUrl, + commonMatchers, + getOrCreatePostageBatch, + BLOCKCHAIN_TRANSACTION_TIMEOUT, + sleep, + WAITING_USABLE_STAMP_TIMEOUT, +} from '../utils' commonMatchers() @@ -9,16 +16,28 @@ describe('Bee Debug class', () => { describe('PostageBatch', () => { it( - 'should create a new postage batch with zero amount', + 'should create a new postage batch with zero amount and be un-usable', async () => { const batchId = await beeDebug.createPostageBatch('0', 17) - const allBatches = await beeDebug.getAllPostageBatch() + const stamp = await beeDebug.getPostageBatch(batchId) + expect(stamp.usable).toEqual(false) + const allBatches = await beeDebug.getAllPostageBatch() expect(allBatches.find(batch => batch.batchID === batchId)).toBeTruthy() }, BLOCKCHAIN_TRANSACTION_TIMEOUT, ) + it( + 'should wait for the stamp to be usable', + async () => { + const batchId = await beeDebug.createPostageBatch('1000', 17, { waitForUsable: true }) + const stamp = await beeDebug.getPostageBatch(batchId) + expect(stamp.usable).toEqual(true) + }, + WAITING_USABLE_STAMP_TIMEOUT + BLOCKCHAIN_TRANSACTION_TIMEOUT, + ) + // TODO: Finish topup and dilute testing https://github.com/ethersphere/bee-js/issues/427 it.skip( 'should topup postage batch', diff --git a/test/utils.ts b/test/utils.ts index 05646750..4086d264 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -431,6 +431,7 @@ export const BIG_FILE_TIMEOUT = 100_000 export const PSS_TIMEOUT = 120_000 export const FEED_TIMEOUT = 120_000 export const BLOCKCHAIN_TRANSACTION_TIMEOUT = 40_000 +export const WAITING_USABLE_STAMP_TIMEOUT = 130_000 export const testChunkPayload = new Uint8Array([1, 2, 3]) // span is the payload length encoded as uint64 little endian