Skip to content

Commit

Permalink
feat: add stamp create, utility get-bee and utility redeem commands
Browse files Browse the repository at this point in the history
  • Loading branch information
Cafe137 committed Dec 4, 2024
1 parent 3525734 commit dbcddf2
Show file tree
Hide file tree
Showing 15 changed files with 1,331 additions and 97 deletions.
891 changes: 872 additions & 19 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@types/inquirer": "^8.2.5",
"@types/jest": "^29.2.3",
"@types/node": "^18.11.9",
"@types/node-fetch": "^2.6.12",
"@typescript-eslint/eslint-plugin": "^5.43.0",
"@typescript-eslint/parser": "^5.43.0",
"eslint": "^8.27.0",
Expand All @@ -63,13 +64,15 @@
"dependencies": {
"@ethersphere/bee-js": "^8.3.0",
"@fairdatasociety/bmt-js": "^2.1.0",
"cafe-utility": "^22.0.0",
"cafe-utility": "^26.2.1",
"chalk": "^2.4.2",
"cli-progress": "^3.11.2",
"ethereumjs-wallet": "^1.0.2",
"ethers": "^5.7.2",
"furious-commander": "^1.7.1",
"inquirer": "^8.2.5",
"mantaray-js": "^1.0.3",
"node-fetch": "^2.7.0",
"ora": "^5.3.0",
"ws": "^8.11.0"
}
Expand Down
2 changes: 1 addition & 1 deletion src/command/feed/print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export class Print extends FeedCommand implements LeafCommand {

if (this.list) {
for (let i = 0; i < numberOfUpdates; i++) {
const indexBytes = Binary.numberToUint64BE(i)
const indexBytes = Binary.numberToUint64(BigInt(i), 'BE')
const identifier = Utils.keccak256Hash(Binary.hexToUint8Array(topic), indexBytes)
const owner = Binary.hexToUint8Array(this.address)
const soc = Binary.uint8ArrayToHex(Utils.keccak256Hash(identifier, owner))
Expand Down
9 changes: 4 additions & 5 deletions src/command/stamp/buy.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { Utils } from '@ethersphere/bee-js'
import { Dates } from 'cafe-utility'
import { Dates, Numbers } from 'cafe-utility'
import { LeafCommand, Option } from 'furious-commander'
import { createSpinner } from '../../utils/spinner'
import { Storage } from '../../utils/storage'
import { createKeyValue } from '../../utils/text'
import { VerbosityLevel } from '../root-command/command-log'
import { StampCommand } from './stamp-command'

export class Buy extends StampCommand implements LeafCommand {
public readonly name = 'buy'

public readonly description = 'Buy postage stamp'
public readonly description = 'Buy postage stamp based on depth and amount'

@Option({
key: 'depth',
Expand Down Expand Up @@ -60,11 +59,11 @@ export class Buy extends StampCommand implements LeafCommand {
await super.init()

const estimatedCost = Utils.getStampCostInBzz(this.depth, Number(this.amount))
const estimatedCapacity = new Storage(Utils.getStampMaximumCapacityBytes(this.depth))
const estimatedCapacity = Numbers.convertBytes(Utils.getStampMaximumCapacityBytes(this.depth))
const estimatedTtl = Utils.getStampTtlSeconds(Number(this.amount))

this.console.log(createKeyValue('Estimated cost', `${estimatedCost.toFixed(3)} xBZZ`))
this.console.log(createKeyValue('Estimated capacity', estimatedCapacity.toString()))
this.console.log(createKeyValue('Estimated capacity', estimatedCapacity))
this.console.log(createKeyValue('Estimated TTL', Dates.secondsToHumanTime(estimatedTtl)))
this.console.log(createKeyValue('Type', this.immutable ? 'Immutable' : 'Mutable'))

Expand Down
123 changes: 123 additions & 0 deletions src/command/stamp/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { Utils } from '@ethersphere/bee-js'
import { Dates, Numbers } from 'cafe-utility'
import { LeafCommand, Option } from 'furious-commander'
import { createSpinner } from '../../utils/spinner'
import { createKeyValue } from '../../utils/text'
import { VerbosityLevel } from '../root-command/command-log'
import { StampCommand } from './stamp-command'

export class Create extends StampCommand implements LeafCommand {
public readonly name = 'create'

public readonly description = 'Create postage stamp in a simple way'

@Option({
key: 'capacity',
description: 'Size of data, e.g. 100MB, 1GB',
type: 'string',
required: false,
})
public capacity!: string

@Option({
key: 'ttl',
description: 'Time to live of the postage stamp, e.g. 1h, 1d, 1w',
type: 'string',
required: false,
})
public ttl!: string

@Option({ key: 'immutable', description: 'Disable stamp reuse', type: 'boolean', default: true })
public immutable!: boolean

@Option({ key: 'label', description: 'Label of the postage stamp' })
public label!: string

public postageBatchId!: string

public async run(): Promise<void> {
super.init()

let capacityInBytes = 0
let ttlInMillis = 0

if (!this.capacity) {
this.console.log('Please provide the capacity of the postage stamp')
this.console.log('This is the size of the data that can be uploaded with this stamp')
this.console.log('Example: 100MB, 1GB')
this.capacity = await this.console.askForValue('Capacity')
this.console.log('')
}

capacityInBytes = Numbers.makeStorage(this.capacity)

if (!this.ttl) {
this.console.log('Please provide the time to live of the postage stamp')
this.console.log('This is the time after which the stamp will expire')
this.console.log('Example: 1h, 1d, 1w')
this.ttl = await this.console.askForValue('TTL')
this.console.log('')
}

ttlInMillis = Dates.make(this.ttl)

const depth = Utils.getDepthForCapacity(capacityInBytes / 1024 ** 3)
const amount = Utils.getAmountForTtl(ttlInMillis / 1000 / 60 / 60 / 24)

this.console.log('You have provided the following parameters:')
this.console.log(createKeyValue('Capacity', Numbers.convertBytes(capacityInBytes)))
this.console.log(createKeyValue('TTL', Dates.secondsToHumanTime(ttlInMillis / 1000)))
this.console.log('')
this.console.log(`Your parameters are now converted to Swarm's internal parameters:`)
this.console.log(createKeyValue('Depth (capacity)', depth))
this.console.log(createKeyValue('Amount (TTL)', amount))

const estimatedCost = Utils.getStampCostInBzz(depth, Number(amount))
const estimatedCapacity = Numbers.convertBytes(Utils.getStampMaximumCapacityBytes(depth))
const estimatedTtl = Utils.getStampTtlSeconds(Number(amount))

this.console.log('')
this.console.log(createKeyValue('Estimated cost', `${estimatedCost.toFixed(3)} xBZZ`))
this.console.log(createKeyValue('Estimated capacity', estimatedCapacity))
this.console.log(createKeyValue('Estimated TTL', Dates.secondsToHumanTime(estimatedTtl)))
this.console.log(createKeyValue('Type', this.immutable ? 'Immutable' : 'Mutable'))

if (this.immutable) {
this.console.info(
'Once an immutable stamp is maxed out, it disallows further content uploads, thereby safeguarding your previously uploaded content from unintentional overwriting.',
)
} else {
this.console.info(
'When a mutable stamp reaches full capacity, it still permits new content uploads. However, this comes with the caveat of overwriting previously uploaded content associated with the same stamp.',
)
}

if (!this.quiet && !this.yes) {
this.yes = await this.console.confirm('Confirm the purchase')
}

if (!this.yes && !this.quiet) {
return
}

const spinner = createSpinner('Creating postage batch. This may take up to 5 minutes.')

if (this.verbosity !== VerbosityLevel.Quiet && !this.curl) {
spinner.start()
}

try {
const batchId = await this.bee.createPostageBatch(amount.toString(), depth, {
label: this.label,
immutableFlag: this.immutable,
waitForUsable: true,
})
spinner.stop()
this.console.quiet(batchId)
this.console.log(createKeyValue('Stamp ID', batchId))
this.postageBatchId = batchId
} finally {
spinner.stop()
}
}
}
3 changes: 2 additions & 1 deletion src/command/stamp/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { GroupCommand } from 'furious-commander'
import { Buy } from './buy'
import { Create } from './create'
import { Dilute } from './dilute'
import { List } from './list'
import { Show } from './show'
Expand All @@ -10,5 +11,5 @@ export class Stamp implements GroupCommand {

public readonly description = 'Buy, list and show postage stamps'

public subCommandClasses = [List, Buy, Show, Dilute, Topup]
public subCommandClasses = [List, Create, Buy, Show, Dilute, Topup]
}
24 changes: 11 additions & 13 deletions src/command/upload.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RedundancyLevel, Tag, Utils } from '@ethersphere/bee-js'
import { System } from 'cafe-utility'
import { Numbers, System } from 'cafe-utility'
import { Presets, SingleBar } from 'cli-progress'
import * as FS from 'fs'
import { Argument, LeafCommand, Option } from 'furious-commander'
Expand All @@ -12,13 +12,10 @@ import { CommandLineError } from '../utils/error'
import { getMime } from '../utils/mime'
import { stampProperties } from '../utils/option'
import { createSpinner } from '../utils/spinner'
import { Storage } from '../utils/storage'
import { createKeyValue, warningSymbol, warningText } from '../utils/text'
import { RootCommand } from './root-command'
import { VerbosityLevel } from './root-command/command-log'

const MAX_UPLOAD_SIZE = new Storage(parseInt(process.env.MAX_UPLOAD_SIZE || '', 10) || 100 * 1000 * 1000) // 100 megabytes

export class Upload extends RootCommand implements LeafCommand {
public readonly name = 'upload'
public readonly alias = 'up'
Expand Down Expand Up @@ -335,19 +332,21 @@ export class Upload extends RootCommand implements LeafCommand {

const currentSetting = Utils.getRedundancyStat(this.redundancy)
const originalSize = await this.getUploadSize()
const originalChunks = Math.ceil(originalSize.getBytes() / 4e3)
const originalChunks = Math.ceil(originalSize / 4e3)
const sizeMultiplier = Utils.approximateOverheadForRedundancyLevel(
originalChunks,
currentSetting.value,
this.encrypt,
)
const newSize = new Storage(originalChunks * 4e3 * (1 + sizeMultiplier))
const extraSize = new Storage(newSize.getBytes() - originalSize.getBytes())
const newSize = originalChunks * 4e3 * (1 + sizeMultiplier)
const extraSize = newSize - originalSize

this.console.log(createKeyValue('Redundancy setting', currentSetting.label))
this.console.log(`This setting will provide ${Math.round(currentSetting.errorTolerance * 100)}% error tolerance.`)
this.console.log(`An additional ${extraSize.toString()} of data will be uploaded approximately.`)
this.console.log(`${originalSize.toString()}${newSize.toString()} (+${extraSize.toString()})`)
this.console.log(`An additional ${Numbers.convertBytes(extraSize)} of data will be uploaded approximately.`)
this.console.log(
`${Numbers.convertBytes(originalSize)}${Numbers.convertBytes(newSize)} (+${Numbers.convertBytes(extraSize)})`,
)

if (!this.yes && !this.quiet) {
const confirmation = await this.console.confirm('Do you want to proceed?')
Expand All @@ -358,7 +357,7 @@ export class Upload extends RootCommand implements LeafCommand {
}
}

private async getUploadSize(): Promise<Storage> {
private async getUploadSize(): Promise<number> {
let size = -1

if (this.stdin) {
Expand All @@ -368,10 +367,9 @@ export class Upload extends RootCommand implements LeafCommand {
size = stats.isDirectory() ? await Utils.getFolderSize(this.path) : stats.size
}

const storage = new Storage(size)
this.console.verbose(`Upload size is approximately ${storage}`)
this.console.verbose(`Upload size is approximately ${Numbers.convertBytes(size)}`)

return storage
return size
}

private hasUnsupportedGatewayOptions(): boolean {
Expand Down
44 changes: 44 additions & 0 deletions src/command/utility/get-bee.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { execSync } from 'child_process'
import { writeFileSync } from 'fs'
import { LeafCommand } from 'furious-commander'
import fetch from 'node-fetch'
import { RootCommand } from '../root-command'

const archTable = {
arm64: 'arm64',
x64: 'amd64',
}

const platformTable = {
win32: 'windows',
darwin: 'darwin',
linux: 'linux',
}

export class GetBee extends RootCommand implements LeafCommand {
public readonly name = 'get-bee'

public readonly description = 'Downloads the Bee binary for the current platform'

public async run(): Promise<void> {
super.init()
const archString = Reflect.get(archTable, process.arch)
const platformString = Reflect.get(platformTable, process.platform)
const suffixString = process.platform === 'win32' ? '.exe' : ''

if (!archString || !platformString) {
throw Error(`Unsupported system: arch=${process.arch} platform=${process.platform}`)
}
const url = `https://github.com/ethersphere/bee/releases/download/v2.3.0/bee-${platformString}-${archString}${suffixString}`
this.console.info(`Downloading Bee from ${url}`)
await fetch(url)
.then(async x => x.arrayBuffer())

Check failure on line 35 in src/command/utility/get-bee.ts

View workflow job for this annotation

GitHub Actions / check (18.x)

Async arrow function has no 'await' expression
.then(x => writeFileSync(`bee${suffixString}`, Buffer.from(x)))
this.console.info('Bee downloaded successfully')
if (process.platform !== 'win32') {

Check failure on line 38 in src/command/utility/get-bee.ts

View workflow job for this annotation

GitHub Actions / check (18.x)

Expected blank line before this statement
this.console.info(`Running chmod +x bee`)
execSync('chmod +x bee')
}
this.console.log('Verify the binary by running ./bee version')
}
}
6 changes: 4 additions & 2 deletions src/command/utility/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import { readFile } from 'fs/promises'
import { GroupCommand } from 'furious-commander'
import { fileExists } from '../../utils'
import { CommandLog } from '../root-command/command-log'
import { GetBee } from './get-bee'
import { Lock } from './lock'
import { Redeem } from './redeem'
import { Unlock } from './unlock'

export class Utility implements GroupCommand {
public readonly name = 'utility'

public readonly description = 'Utility commands for managing wallets'
public readonly description = 'Utility commands related to Swarm and wallets'

public subCommandClasses = [Lock, Unlock]
public subCommandClasses = [Lock, Unlock, GetBee, Redeem]
}

export async function createWallet(pathOrPrivateKey: string, console: CommandLog): Promise<Wallet> {
Expand Down
Loading

0 comments on commit dbcddf2

Please sign in to comment.