Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ npm run start
```sh
export POSTAGE_DEPTH=20
export POSTAGE_AMOUNT=1000000
export BEE_DEBUG_API_URL=http://localhost:1635
export BEE_DEBUG_API_URL=http://localhost:1635 (optional)

npm run start
```
Expand All @@ -111,7 +111,7 @@ export POSTAGE_EXTENDSTTL=true
export POSTAGE_TTL_MIN=60
export POSTAGE_DEPTH=20
export POSTAGE_AMOUNT=1000000
export BEE_DEBUG_API_URL=http://localhost:1635
export BEE_DEBUG_API_URL=http://localhost:1635 (optional)

npm run start
```
Expand Down
74 changes: 55 additions & 19 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isInteger, isBoolean } from './utils'
import { isInteger, assertBoolean, assertInteger, assertDecimal } from './utils'

export interface AppConfig {
beeApiUrl: string
Expand Down Expand Up @@ -147,15 +147,19 @@ export function getStampsConfig({
POSTAGE_EXTENDSTTL,
POSTAGE_EXTENDS_CAPACITY,
}: EnvironmentVariables = {}): StampsConfig | undefined {
if (POSTAGE_REFRESH_PERIOD) {
assertInteger(POSTAGE_REFRESH_PERIOD)
}

const refreshPeriod = Number(POSTAGE_REFRESH_PERIOD || DEFAULT_POSTAGE_REFRESH_PERIOD)
Comment thread
luissanchez0305 marked this conversation as resolved.
const beeDebugApiUrl = BEE_DEBUG_API_URL || DEFAULT_BEE_DEBUG_API_URL

// Start in hardcoded mode
if (POSTAGE_STAMP) return { mode: 'hardcoded', stamp: POSTAGE_STAMP }
// Start autobuy
else if (
(isBoolean(POSTAGE_EXTENDSTTL) && POSTAGE_EXTENDSTTL === 'true') ||
(isBoolean(POSTAGE_EXTENDS_CAPACITY) && POSTAGE_EXTENDS_CAPACITY === 'true')
(assertBoolean(POSTAGE_EXTENDSTTL) && POSTAGE_EXTENDSTTL === 'true') ||
(assertBoolean(POSTAGE_EXTENDS_CAPACITY) && POSTAGE_EXTENDS_CAPACITY === 'true')
Comment thread
luissanchez0305 marked this conversation as resolved.
Outdated
) {
return createExtendsStampsConfig(
POSTAGE_EXTENDSTTL,
Expand All @@ -167,31 +171,57 @@ export function getStampsConfig({
refreshPeriod,
beeDebugApiUrl,
)
} else if (POSTAGE_DEPTH && POSTAGE_AMOUNT && BEE_DEBUG_API_URL) {
return {
mode: 'autobuy',
depth: Number(POSTAGE_DEPTH),
amount: POSTAGE_AMOUNT,
usageThreshold: Number(POSTAGE_USAGE_THRESHOLD || DEFAULT_POSTAGE_USAGE_THRESHOLD),
usageMax: Number(POSTAGE_USAGE_MAX || DEFAULT_POSTAGE_USAGE_MAX),
ttlMin: Number(POSTAGE_TTL_MIN || (refreshPeriod / 1000) * 5),
} else if (POSTAGE_DEPTH && POSTAGE_AMOUNT) {
return createAutobuyStampsConfig(
POSTAGE_DEPTH,
POSTAGE_AMOUNT,
POSTAGE_USAGE_THRESHOLD,
POSTAGE_USAGE_MAX,
POSTAGE_TTL_MIN,
refreshPeriod,
beeDebugApiUrl,
}
)
}
// Missing one of the variables needed for the autobuy or extends TTL
else if (POSTAGE_DEPTH || POSTAGE_AMOUNT || POSTAGE_TTL_MIN || BEE_DEBUG_API_URL) {
else if (POSTAGE_DEPTH || POSTAGE_AMOUNT || POSTAGE_TTL_MIN) {
throw new Error(
`config: please provide POSTAGE_DEPTH=${POSTAGE_DEPTH}, POSTAGE_AMOUNT=${POSTAGE_AMOUNT}, POSTAGE_TTL_MIN=${POSTAGE_TTL_MIN} ${
POSTAGE_EXTENDSTTL === 'true' ? 'at least 60 seconds ' : ''
}or BEE_DEBUG_API_URL=${BEE_DEBUG_API_URL} for the feature to work`,
`config: please provide POSTAGE_DEPTH=${POSTAGE_DEPTH}, POSTAGE_AMOUNT=${POSTAGE_AMOUNT} or POSTAGE_TTL_MIN=${POSTAGE_TTL_MIN} for the feature to work`,
)
}

// Stamps rewrite is disabled
return undefined
}

export function createAutobuyStampsConfig(
POSTAGE_DEPTH: string,
POSTAGE_AMOUNT: string,
POSTAGE_USAGE_THRESHOLD: string | undefined,
POSTAGE_USAGE_MAX: string | undefined,
POSTAGE_TTL_MIN: string | undefined,
refreshPeriod: number,
beeDebugApiUrl: string,
): StampsConfigAutobuy {
// Missing one of the variables needed for the autobuy
if (!isInteger(POSTAGE_DEPTH) || !isInteger(POSTAGE_AMOUNT)) {
throw new Error(
`config: please provide valid values for POSTAGE_DEPTH, POSTAGE_AMOUNT for the autobuy feature to work.
Current state are POSTAGE_DEPTH=${POSTAGE_DEPTH} and POSTAGE_AMOUNT=${POSTAGE_AMOUNT}`,
)
}

return {
mode: 'autobuy',
depth: Number(POSTAGE_DEPTH),
amount: POSTAGE_AMOUNT,
usageThreshold: Number(POSTAGE_USAGE_THRESHOLD || DEFAULT_POSTAGE_USAGE_THRESHOLD),
usageMax: Number(POSTAGE_USAGE_MAX || DEFAULT_POSTAGE_USAGE_MAX),
ttlMin: Number(POSTAGE_TTL_MIN || (refreshPeriod / 1000) * 5),
refreshPeriod,
beeDebugApiUrl,
}
}

export function createExtendsStampsConfig(
POSTAGE_EXTENDSTTL: string | undefined,
POSTAGE_EXTENDS_CAPACITY: string | undefined,
Expand All @@ -203,6 +233,7 @@ export function createExtendsStampsConfig(
beeDebugApiUrl: string,
): StampsConfigExtends {
if (
assertBoolean(POSTAGE_EXTENDSTTL) &&
POSTAGE_EXTENDSTTL === 'true' &&
(Number(POSTAGE_TTL_MIN) < MINIMAL_EXTENDS_TTL_VALUE ||
!isInteger(POSTAGE_AMOUNT) ||
Expand All @@ -211,12 +242,17 @@ export function createExtendsStampsConfig(
) {
throw new Error(
`config: to extends stamps TTL please provide POSTAGE_TTL_MIN bigger than ${MINIMAL_EXTENDS_TTL_VALUE}, valid values for
POSTAGE_AMOUNT, POSTAGE_TTL_MIN, POSTAGE_DEPTH. Current states are
POSTAGE_TTL_MIN=${POSTAGE_TTL_MIN}, POSTAGE_AMOUNT=${POSTAGE_AMOUNT}, POSTAGE_TTL_MIN=${POSTAGE_TTL_MIN} and POSTAGE_DEPTH=${POSTAGE_DEPTH}`,
POSTAGE_AMOUNT, POSTAGE_TTL_MIN, POSTAGE_DEPTH. Current states are POSTAGE_TTL_MIN=${POSTAGE_TTL_MIN},
POSTAGE_AMOUNT=${POSTAGE_AMOUNT} and POSTAGE_DEPTH=${POSTAGE_DEPTH}`,
)
}

if (POSTAGE_EXTENDS_CAPACITY === 'true' && POSTAGE_USAGE_THRESHOLD && !isInteger(POSTAGE_USAGE_THRESHOLD)) {
if (
assertBoolean(POSTAGE_EXTENDS_CAPACITY) &&
POSTAGE_EXTENDS_CAPACITY === 'true' &&
POSTAGE_USAGE_THRESHOLD &&
!assertDecimal(POSTAGE_USAGE_THRESHOLD)
) {
throw new Error(
`config: to extends capacity please provide valid number for POSTAGE_USAGE_THRESHOLD. Current states is
POSTAGE_USAGE_THRESHOLD=${POSTAGE_USAGE_THRESHOLD}`,
Expand Down
2 changes: 1 addition & 1 deletion src/stamps/autobuy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class AutoBuyStampsManager extends BaseStampManager implements StampsMana
super()
// Autobuy mode
const refreshStamps = async () => this.refreshStamps(config, new BeeDebug(config.beeDebugApiUrl))
this.startFeature(config, refreshStamps)
this.start(config, refreshStamps)
}

/**
Expand Down
15 changes: 7 additions & 8 deletions src/stamps/base.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { PostageBatch } from '@ethersphere/bee-js'
import { stampGetCounter, stampGetErrorCounter } from './counters'
import { logger } from '../logger'
import { ERROR_NO_STAMP, StampsConfig, StampsConfigAutobuy, StampsConfigExtends } from '../config'
import { ERROR_NO_STAMP, StampsConfig } from '../config'

export interface StampsManager {
start?: (config: StampsConfig) => Promise<void>
start: (config: StampsConfig, refreshStamps: () => void) => Promise<void>
stop: () => void
postageStamp: () => string
}

export class BaseStampManager implements StampsManager {
export class BaseStampManager {
Comment thread
luissanchez0305 marked this conversation as resolved.
private interval?: ReturnType<typeof setInterval>
public stamp?: string
public usableStamps?: PostageBatch[]
Comment on lines +7 to +15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think these needs to be public right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be part of the base class. This is solely Autobuy related.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still applicable.

Expand Down Expand Up @@ -45,14 +45,13 @@ export class BaseStampManager implements StampsManager {
/**
* Start the manager in either hardcoded or autobuy mode
*/
public async startFeature(
config: StampsConfigAutobuy | StampsConfigExtends,
refreshStamps: () => void,
): Promise<void> {
async start(config: StampsConfig, refreshStamps: () => void): Promise<void> {
this.stop()
refreshStamps()

this.interval = setInterval(refreshStamps, config.refreshPeriod)
if (config.mode === 'autobuy' || config.mode === 'extends') {
this.interval = setInterval(refreshStamps, config.refreshPeriod)
}
}

stop(): void {
Expand Down
22 changes: 10 additions & 12 deletions src/stamps/extends.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import { BaseStampManager, StampsManager } from './base'
*
* @returns Filtered stamps soltered by usage
*/
export function filterUsableStampsExtendsTTL(stamps: PostageBatch[], minTimeThreshold: number): PostageBatch[] {
export function filterUsableStampsExtendsTTL(stamps: PostageBatch[]): PostageBatch[] {
const usableStamps = stamps
// filter to get stamps that have the right depth, amount and are not fully used or expired
Comment thread
luissanchez0305 marked this conversation as resolved.
Outdated
.filter(s => s.usable && s.batchTTL < minTimeThreshold)
.filter(s => s.usable)
// sort the stamps by usage
Comment thread
luissanchez0305 marked this conversation as resolved.
Outdated
.sort((a, b) => (a.batchTTL > b.batchTTL ? 1 : -1))

Expand Down Expand Up @@ -67,7 +67,7 @@ export class ExtendsStampManager extends BaseStampManager implements StampsManag
super()
// Autobuy mode
const refreshStamps = async () => this.refreshStamps(config, new BeeDebug(config.beeDebugApiUrl))
this.startFeature(config, refreshStamps)
this.start(config, refreshStamps)
Comment thread
luissanchez0305 marked this conversation as resolved.
Outdated
}

completeTopUp(extendsTypeFeature: 'ttl' | 'capacity', stamp: PostageBatch) {
Expand All @@ -83,7 +83,7 @@ export class ExtendsStampManager extends BaseStampManager implements StampsManag

async verifyUsableStamps(beeDebug: BeeDebug, ttlMin: number, amount: string) {
for (const stamp of this.usableStamps!) {
if (ttlMin > 0 && !this.topingUpStamps.includes(stamp.batchID) && amount !== '0') {
if (!this.topingUpStamps.includes(stamp.batchID) && amount !== '0') {
this.topingUpStamps.push(stamp.batchID)
logger.info(`extending postage stamp TTL ${stamp.batchID}`)

Expand All @@ -102,8 +102,6 @@ export class ExtendsStampManager extends BaseStampManager implements StampsManag
}

private async refreshStamps(config: StampsConfigExtends, beeDebug: BeeDebug): Promise<void> {
let usableStampsExtendsTTL: PostageBatch[] = []
let usableStampsExtendsCapacity: PostageBatch[] = []
stampCheckCounter.inc()
logger.info('checking postage stamps')

Expand All @@ -113,27 +111,27 @@ export class ExtendsStampManager extends BaseStampManager implements StampsManag

// Get all usable stamps sorted by usage from most used to least
if (!this.isBuyingStamp && enableTtl) {
const minTimeThreshold = ttlMin + refreshPeriod / 1000
usableStampsExtendsTTL = filterUsableStampsExtendsTTL(stamps, minTimeThreshold)
const usableStampsSortByTTL = filterUsableStampsExtendsTTL(stamps)

if (depth > 0 && usableStampsExtendsTTL.length === 0) {
if (usableStampsSortByTTL.length === 0) {
this.isBuyingStamp = true
try {
const { stamp: newStamp } = await buyNewStamp(depth, amount, beeDebug)

// Add the bought postage stamp
usableStampsExtendsTTL.push(newStamp)
usableStampsSortByTTL.push(newStamp)
} finally {
this.isBuyingStamp = false
}
} else {
this.usableStamps = usableStampsExtendsTTL
const minTimeThreshold = ttlMin + refreshPeriod / 1000
this.usableStamps = usableStampsSortByTTL.filter(s => s.batchTTL < minTimeThreshold)
await this.verifyUsableStamps(beeDebug, ttlMin, amount)
}
}

if (enableCapacity) {
usableStampsExtendsCapacity = filterUsableStampsExtendsCapacity(stamps, usageThreshold)
const usableStampsExtendsCapacity = filterUsableStampsExtendsCapacity(stamps, usageThreshold)

for (const stamp of usableStampsExtendsCapacity) {
try {
Expand Down
33 changes: 26 additions & 7 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,15 @@ export function getUsage({ utilization, depth, bucketDepth }: PostageBatch): num
* @param value
* @returns boolean
*/
export function isBoolean(value: unknown): boolean {
return (
typeof value === 'boolean' ||
(typeof value === 'string' && (value === 'true' || value === 'false')) ||
(typeof value === 'number' && (value === 1 || value === 0))
)
export function assertBoolean(value: unknown): boolean {
Comment thread
luissanchez0305 marked this conversation as resolved.
Outdated
if (
(typeof value === 'string' && value !== 'true' && value !== 'false') ||
(typeof value === 'number' && value !== 1 && value !== 0)
Comment thread
luissanchez0305 marked this conversation as resolved.
Outdated
) {
throw Error(`Invalid value pass as boolean: ${value}`)
}

return true
}

/**
Expand All @@ -70,14 +73,30 @@ export function isBoolean(value: unknown): boolean {
*/
export function isInteger(value: unknown): value is number | string | NumberString {
return (
typeof value === 'string' ||
(typeof value === 'string' && /^-?(0|[1-9][0-9]*)$/g.test(value)) ||
(typeof value === 'number' &&
value > Number.MIN_SAFE_INTEGER &&
value < Number.MAX_SAFE_INTEGER &&
Number.isInteger(value))
)
}

export function assertInteger(value: unknown, name = 'value'): asserts value is number | NumberString {
if (!isInteger(value)) throw new TypeError(`${name} is not a valid integer`)
}

export function assertDecimal(value: unknown, name = 'value'): boolean {
if (typeof value === 'number') {
value = value.toString()
}

if (typeof value !== 'string' || (typeof value === 'string' && !/^[-+]?[0]+\.[0-9]+$/g.test(value))) {
Comment thread
luissanchez0305 marked this conversation as resolved.
Outdated
throw new TypeError(`${name} is not a valid decimal`)
}

return true
}

/**
* Buy new postage stamp and wait until it is usable
*
Expand Down
8 changes: 1 addition & 7 deletions test/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,24 +188,18 @@ describe('getStampsConfig', () => {
})

const throwValues: EnvironmentVariables[] = [
{ BEE_DEBUG_API_URL },
{ POSTAGE_DEPTH },
{ POSTAGE_AMOUNT },
{ BEE_DEBUG_API_URL, POSTAGE_DEPTH },
{ BEE_DEBUG_API_URL, POSTAGE_AMOUNT },
{ POSTAGE_DEPTH, POSTAGE_AMOUNT },
]

throwValues.forEach(v => {
it(`should throw for ${JSON.stringify(v)}`, () => {
expect(() => {
getStampsConfig(v)
}).toThrowError(
`config: please provide POSTAGE_DEPTH=${v.POSTAGE_DEPTH}, POSTAGE_AMOUNT=${v.POSTAGE_AMOUNT}, POSTAGE_TTL_MIN=${
v.POSTAGE_TTL_MIN
} ${v.POSTAGE_EXTENDSTTL === 'true' ? 'at least 60 seconds ' : ''}or BEE_DEBUG_API_URL=${
v.BEE_DEBUG_API_URL
} for the feature to work`,
`config: please provide POSTAGE_DEPTH=${v.POSTAGE_DEPTH}, POSTAGE_AMOUNT=${v.POSTAGE_AMOUNT} or POSTAGE_TTL_MIN=${v.POSTAGE_TTL_MIN} for the feature to work`,
)
})
})
Expand Down
3 changes: 2 additions & 1 deletion test/stamps.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ describe('extendsStampsTTL', () => {

const { ttlMin, refreshPeriod } = stampsConfig as StampsConfigExtends
const minTimeThreshold = ttlMin + refreshPeriod / 1000
const res = filterUsableStampsExtendsTTL(stamps, minTimeThreshold)
const usableStampsSortByTTL = filterUsableStampsExtendsTTL(stamps)
const res = usableStampsSortByTTL.filter(s => s.batchTTL < minTimeThreshold)
expect(0).toEqual(res.length)
})

Expand Down