Skip to content

Commit

Permalink
feat: topup and dilute batch methods (#424)
Browse files Browse the repository at this point in the history
  • Loading branch information
AuHau committed Oct 13, 2021
1 parent 3134c50 commit 7bf2135
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 24 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 47 additions & 5 deletions src/bee-debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,6 @@ import * as stamps from './modules/debug/stamps'
import type { Options as KyOptions } from 'ky-universal'
import { makeDefaultKy, wrapRequestClosure, wrapResponseClosure } from './utils/http'

/**
* The BeeDebug class provides a way of interacting with the Bee debug APIs based on the provided url
*
* @param url URL of a running Bee node
*/
export class BeeDebug {
/**
* URL on which is the Debug API of Bee node exposed
Expand Down Expand Up @@ -445,6 +440,53 @@ export class BeeDebug {
return stamps.createPostageBatch(this.getKy(options), amount, depth, options)
}

/**
* Topup a fresh amount of BZZ to given Postage Batch.
*
* For better understanding what each parameter means and what are the optimal values please see
* [Bee docs - Keep your data alive / Postage stamps](https://docs.ethswarm.org/docs/access-the-swarm/keep-your-data-alive).
*
* **WARNING: THIS CREATES TRANSACTIONS THAT SPENDS MONEY**
*
* @param postageBatchId Batch ID
* @param amount Amount to be added to the batch
* @param options Request options
*
* @see [Bee docs - Keep your data alive / Postage stamps](https://docs.ethswarm.org/docs/access-the-swarm/keep-your-data-alive)
* @see [Bee Debug API reference - `PATCH /stamps/topup/${id}/${amount}`](https://docs.ethswarm.org/debug-api/#tag/Postage-Stamps/paths/~1stamps~1topup~1{id}~1{amount}/patch)
*/
async topUpBatch(postageBatchId: BatchId | string, amount: NumberString, options?: RequestOptions): Promise<void> {
assertRequestOptions(options)
assertNonNegativeInteger(amount, 'Amount')
assertBatchId(postageBatchId)

await stamps.topUpBatch(this.getKy(options), postageBatchId, amount)
}

/**
* Dilute given Postage Batch with new depth (that has to be bigger then the original depth), which allows
* the Postage Batch to be used for more chunks.
*
* For better understanding what each parameter means and what are the optimal values please see
* [Bee docs - Keep your data alive / Postage stamps](https://docs.ethswarm.org/docs/access-the-swarm/keep-your-data-alive).
*
* **WARNING: THIS CREATES TRANSACTIONS THAT SPENDS MONEY**
*
* @param postageBatchId Batch ID
* @param depth Amount to be added to the batch
* @param options Request options
*
* @see [Bee docs - Keep your data alive / Postage stamps](https://docs.ethswarm.org/docs/access-the-swarm/keep-your-data-alive)
* @see [Bee Debug API reference - `PATCH /stamps/topup/${id}/${amount}`](https://docs.ethswarm.org/debug-api/#tag/Postage-Stamps/paths/~1stamps~1topup~1{id}~1{amount}/patch)
*/
async diluteBatch(postageBatchId: BatchId | string, depth: number, options?: RequestOptions): Promise<void> {
assertRequestOptions(options)
assertNonNegativeInteger(depth, 'Depth')
assertBatchId(postageBatchId)

await stamps.diluteBatch(this.getKy(options), postageBatchId, depth)
}

/**
* Return details for specific postage batch.
*
Expand Down
24 changes: 22 additions & 2 deletions src/modules/debug/stamps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface GetAllStampsResponse {
stamps: DebugPostageBatch[]
}

interface CreateStampResponse {
interface StampResponse {
batchID: BatchId
}

Expand Down Expand Up @@ -64,7 +64,7 @@ export async function createPostageBatch(
headers.immutable = String(options.immutableFlag)
}

const response = await http<CreateStampResponse>(ky, {
const response = await http<StampResponse>(ky, {
method: 'post',
path: `${STAMPS_ENDPOINT}/${amount}/${depth}`,
responseType: 'json',
Expand All @@ -74,3 +74,23 @@ export async function createPostageBatch(

return response.data.batchID
}

export async function topUpBatch(ky: Ky, id: string, amount: NumberString): Promise<BatchId> {
const response = await http<StampResponse>(ky, {
method: 'patch',
path: `${STAMPS_ENDPOINT}/topup/${id}/${amount}`,
responseType: 'json',
})

return response.data.batchID
}

export async function diluteBatch(ky: Ky, id: string, depth: number): Promise<BatchId> {
const response = await http<StampResponse>(ky, {
method: 'patch',
path: `${STAMPS_ENDPOINT}/dilute/${id}/${depth}`,
responseType: 'json',
})

return response.data.batchID
}
6 changes: 3 additions & 3 deletions test/integration/bee-class.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
FEED_TIMEOUT,
getPostageBatch,
makeTestTarget,
POSTAGE_BATCH_TIMEOUT,
BLOCKCHAIN_TRANSACTION_TIMEOUT,
PSS_TIMEOUT,
randomByteArray,
sleep,
Expand Down Expand Up @@ -603,7 +603,7 @@ describe('Bee class', () => {

expect(allBatches.find(batch => batch.batchID === batchId)).toBeTruthy()
},
POSTAGE_BATCH_TIMEOUT,
BLOCKCHAIN_TRANSACTION_TIMEOUT,
)

it(
Expand All @@ -616,7 +616,7 @@ describe('Bee class', () => {
expect(allBatches.find(batch => batch.immutableFlag === true)).toBeTruthy()
expect(allBatches.find(batch => batch.immutableFlag === false)).toBeTruthy()
},
POSTAGE_BATCH_TIMEOUT * 2,
BLOCKCHAIN_TRANSACTION_TIMEOUT * 2,
)

it('should have all properties', async () => {
Expand Down
35 changes: 32 additions & 3 deletions test/integration/bee-debug-class.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BeeArgumentError, BeeDebug } from '../../src'
import { beeDebugUrl, commonMatchers, POSTAGE_BATCH_TIMEOUT } from '../utils'
import { beeDebugUrl, commonMatchers, getOrCreatePostageBatch, BLOCKCHAIN_TRANSACTION_TIMEOUT, sleep } from '../utils'

commonMatchers()

Expand All @@ -16,7 +16,36 @@ describe('Bee Debug class', () => {

expect(allBatches.find(batch => batch.batchID === batchId)).toBeTruthy()
},
POSTAGE_BATCH_TIMEOUT,
BLOCKCHAIN_TRANSACTION_TIMEOUT,
)

// TODO: Finish topup and dilute testing https://github.com/ethersphere/bee-js/issues/427
it.skip(
'should topup postage batch',
async () => {
const batch = await getOrCreatePostageBatch(undefined, undefined, false)

await beeDebug.topUpBatch(batch.batchID, '10')

await sleep(4000)
const batchDetails = await beeDebug.getPostageBatch(batch.batchID)
const newAmount = (parseInt(batch.amount) + 10).toString()
expect(batchDetails.amount).toEqual(newAmount)
},
BLOCKCHAIN_TRANSACTION_TIMEOUT * 3,
)

// TODO: Finish topup and dilute testing https://github.com/ethersphere/bee-js/issues/427
it.skip(
'should dilute postage batch',
async () => {
const batch = await getOrCreatePostageBatch(undefined, 17, false)
await beeDebug.diluteBatch(batch.batchID, batch.depth + 2)

const batchDetails = await beeDebug.getPostageBatch(batch.batchID)
expect(batchDetails.depth).toEqual(batch.depth + 2)
},
BLOCKCHAIN_TRANSACTION_TIMEOUT * 2,
)

it(
Expand All @@ -29,7 +58,7 @@ describe('Bee Debug class', () => {
expect(allBatches.find(batch => batch.immutableFlag === true)).toBeTruthy()
expect(allBatches.find(batch => batch.immutableFlag === false)).toBeTruthy()
},
POSTAGE_BATCH_TIMEOUT * 2,
BLOCKCHAIN_TRANSACTION_TIMEOUT * 2,
)

it('should have all properties', async () => {
Expand Down
114 changes: 104 additions & 10 deletions test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Readable } from 'stream'
import type { Ky, BeeGenericResponse, Reference, Address, BatchId } from '../src/types'
import ky from 'ky-universal'
import { ReadableStream } from 'web-streams-polyfill/ponyfill'

import type { Ky, BeeGenericResponse, Reference, Address, BatchId, DebugPostageBatch } from '../src/types'
import { bytesToHex, HexString } from '../src/utils/hex'
import { deleteChunkFromLocalStorage } from '../src/modules/debug/chunk'
import { BeeResponseError } from '../src'
import { ChunkAddress } from '../src/chunk/cac'
import { assertBytes } from '../src/utils/bytes'
import ky from 'ky-universal'
import { ReadableStream } from 'web-streams-polyfill/ponyfill'
import * as stamps from '../src/modules/debug/stamps'

declare global {
// eslint-disable-next-line @typescript-eslint/no-namespace
Expand Down Expand Up @@ -238,7 +240,7 @@ export function beeDebugUrl(): string {
}

export function beeDebugKy(): Ky {
return ky.create({ prefixUrl: beeDebugUrl() })
return ky.create({ prefixUrl: beeDebugUrl(), timeout: false })
}

/**
Expand All @@ -249,7 +251,7 @@ export function beePeerDebugUrl(): string {
}

export function beePeerDebugKy(): Ky {
return ky.create({ prefixUrl: beePeerDebugUrl() })
return ky.create({ prefixUrl: beePeerDebugUrl(), timeout: false })
}

/**
Expand Down Expand Up @@ -290,6 +292,97 @@ export function shorten(inputStr: unknown, len = 17): string {
return `${str.slice(0, 6)}...${str.slice(-6)} (length: ${str.length})`
}

async function timeout(ms: number, message = 'Execution reached timeout!'): Promise<Error> {
await sleep(ms)
throw new Error(message)
}

export async function waitForBatchToBeUsable(batchId: string, pollingInterval = 200): Promise<void> {
await Promise.race([
timeout(USABLE_TIMEOUT, 'Awaiting of usable postage batch timed out!'),
async () => {
let stamp

do {
await sleep(pollingInterval)
stamp = await stamps.getPostageBatch(beeDebugKy(), batchId as BatchId)
} while (!stamp.usable)
},
])
}

const DEFAULT_BATCH_AMOUNT = '1'
const DEFAULT_BATCH_DEPTH = 17

/**
* Returns already existing batch or will create one.
*
* If some specification is passed then it is guaranteed that the batch will have this property(ies)
*
* @param amount
* @param depth
* @param immutable
*/
export async function getOrCreatePostageBatch(
amount?: string,
depth?: number,
immutable?: boolean,
): Promise<DebugPostageBatch> {
// Non-usable stamps are ignored by Bee
const allUsableStamps = (await stamps.getAllPostageBatches(beeDebugKy())).filter(stamp => stamp.usable)

if (allUsableStamps.length === 0) {
const batchId = await stamps.createPostageBatch(
beeDebugKy(),
amount ?? DEFAULT_BATCH_AMOUNT,
depth ?? DEFAULT_BATCH_DEPTH,
)

await waitForBatchToBeUsable(batchId)

return stamps.getPostageBatch(beeDebugKy(), batchId)
}

// User does not want any specific batch, lets give him the first one
if (amount === undefined && depth === undefined && immutable === undefined) {
return allUsableStamps[0]
}

// User wants some specific batch
for (const stamp of allUsableStamps) {
let meetingAllCriteria = false

if (amount !== undefined) {
meetingAllCriteria = amount === stamp.amount
} else {
meetingAllCriteria = true
}

if (depth !== undefined) {
meetingAllCriteria = meetingAllCriteria && depth === stamp.depth
}

if (immutable !== undefined) {
meetingAllCriteria = meetingAllCriteria && immutable === stamp.immutableFlag
}

if (meetingAllCriteria) {
return stamp
}
}

// No stamp meeting the criteria was found ==> we need to create a new one
const batchId = await stamps.createPostageBatch(
beeDebugKy(),
amount ?? DEFAULT_BATCH_AMOUNT,
depth ?? DEFAULT_BATCH_DEPTH,
)

await waitForBatchToBeUsable(batchId)

return stamps.getPostageBatch(beeDebugKy(), batchId)
}

export function makeTestTarget(target: string): string {
return target.slice(0, 2)
}
Expand All @@ -306,11 +399,12 @@ export const createdResponse: BeeGenericResponse = {
message: 'Created',
}

export const ERR_TIMEOUT = 40000
export const BIG_FILE_TIMEOUT = 100000
export const PSS_TIMEOUT = 120000
export const FEED_TIMEOUT = 120000
export const POSTAGE_BATCH_TIMEOUT = 40000
const USABLE_TIMEOUT = 7_000
export const ERR_TIMEOUT = 40_000
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 testChunkPayload = new Uint8Array([1, 2, 3])
// span is the payload length encoded as uint64 little endian
Expand Down

0 comments on commit 7bf2135

Please sign in to comment.