Skip to content

Commit

Permalink
Setting up transaction timer to handle edgecase where there is an err…
Browse files Browse the repository at this point in the history
…or calculating spendPostTime
  • Loading branch information
patnir committed Mar 21, 2024
1 parent ca1029b commit 0713fb3
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 21 deletions.
12 changes: 7 additions & 5 deletions ironfish-cli/src/commands/wallet/notes/combine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,11 +303,13 @@ export class CombineNotesCommand extends IronfishCommand {

const transactionTimer = new TransactionTimer(spendPostTime, raw)

this.log(
`Time to combine: ${TimeUtils.renderSpan(transactionTimer.getEstimateInMs(), {
hideMilliseconds: true,
})}`,
)
if (spendPostTime > 0) {
this.log(
`Time to combine: ${TimeUtils.renderSpan(transactionTimer.getEstimateInMs(), {
hideMilliseconds: true,
})}`,
)
}

if (!flags.confirm) {
const confirmed = await CliUx.ux.confirm('Do you confirm (Y/N)?')
Expand Down
18 changes: 16 additions & 2 deletions ironfish-cli/src/commands/wallet/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { selectAsset } from '../../utils/asset'
import { promptCurrency } from '../../utils/currency'
import { getExplorer } from '../../utils/explorer'
import { selectFee } from '../../utils/fees'
import { getSpendPostTimeInMs } from '../../utils/spendPostTime'
import { TransactionTimer } from '../../utils/timer'
import { displayTransactionSummary, watchTransaction } from '../../utils/transaction'

export class Send extends IronfishCommand {
Expand Down Expand Up @@ -103,6 +105,11 @@ export class Send extends IronfishCommand {
description: 'The note hashes to include in the transaction',
multiple: true,
}),
benchmark: Flags.boolean({
hidden: true,
default: false,
description: 'Force run the benchmark to measure the time to combine 1 note',
}),
}

async start(): Promise<void> {
Expand Down Expand Up @@ -239,14 +246,20 @@ export class Send extends IronfishCommand {

displayTransactionSummary(raw, assetId, amount, from, to, memo)

const spendPostTime = await getSpendPostTimeInMs(this.sdk, client, from, flags.benchmark)

const transactionTimer = new TransactionTimer(spendPostTime, raw)

transactionTimer.displayEstimate()

if (!flags.confirm) {
const confirmed = await CliUx.ux.confirm('Do you confirm (Y/N)?')
if (!confirmed) {
this.error('Transaction aborted.')
}
}

CliUx.ux.action.start('Sending the transaction')
transactionTimer.start()

const response = await client.wallet.postTransaction({
transaction: RawTransactionSerde.serialize(raw).toString('hex'),
Expand All @@ -256,7 +269,7 @@ export class Send extends IronfishCommand {
const bytes = Buffer.from(response.content.transaction, 'hex')
const transaction = new Transaction(bytes)

CliUx.ux.action.stop()
transactionTimer.end()

if (response.content.accepted === false) {
this.warn(
Expand All @@ -268,6 +281,7 @@ export class Send extends IronfishCommand {
this.warn(`Transaction '${transaction.hash().toString('hex')}' failed to broadcast`)
}

this.log()
this.log(`Sent ${CurrencyUtils.renderIron(amount, true, assetId)} to ${to} from ${from}`)
this.log(`Hash: ${transaction.hash().toString('hex')}`)
this.log(`Fee: ${CurrencyUtils.renderIron(transaction.fee(), true)}`)
Expand Down
52 changes: 52 additions & 0 deletions ironfish-cli/src/utils/notes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { Asset } from '@ironfish/rust-nodejs'
import { Assert, RpcClient } from '@ironfish/sdk'

async function getNoteTreeSize(client: RpcClient) {
const getCurrentBlock = await client.chain.getChainInfo()

const currentBlockSequence = parseInt(getCurrentBlock.content.currentBlockIdentifier.index)

const getBlockResponse = await client.chain.getBlock({
sequence: currentBlockSequence,
})

Assert.isNotNull(getBlockResponse.content.block.noteSize)

const config = await client.config.getConfig()

// Adding a buffer to avoid a mismatch between confirmations used to load notes and confirmations used when creating witnesses to spend them
return getBlockResponse.content.block.noteSize - (config.content.confirmations || 2)
}

export async function fetchNotes(client: RpcClient, account: string, notesToCombine: number) {
const noteSize = await getNoteTreeSize(client)

const getNotesResponse = await client.wallet.getNotes({
account,
pageSize: notesToCombine,
filter: {
assetId: Asset.nativeId().toString('hex'),
spent: false,
},
})

// filtering notes by noteSize and sorting them by value in ascending order
const notes = getNotesResponse.content.notes
.filter((note) => {
if (!note.index) {
return false
}
return note.index < noteSize
})
.sort((a, b) => {
if (a.value < b.value) {
return -1
}
return 1
})

return notes
}
36 changes: 23 additions & 13 deletions ironfish-cli/src/utils/spendPostTime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,29 @@ export async function getSpendPostTimeInMs(
account: string,
forceBenchmark: boolean,
): Promise<number> {
let spendPostTime = sdk.internal.get('spendPostTime')
try {
let spendPostTime = sdk.internal.get('spendPostTime')

const spendPostTimeAt = sdk.internal.get('spendPostTimeAt')
const spendPostTimeAt = sdk.internal.get('spendPostTimeAt')

const shouldbenchmark =
forceBenchmark ||
spendPostTime <= 0 ||
Date.now() - spendPostTimeAt > 1000 * 60 * 60 * 24 * 30 // 1 month
const shouldbenchmark =
forceBenchmark ||
spendPostTime <= 0 ||
Date.now() - spendPostTimeAt > 1000 * 60 * 60 * 24 * 30 // 1 month

if (shouldbenchmark) {
spendPostTime = await benchmarkSpendPostTime(client, account)
if (shouldbenchmark) {
spendPostTime = await benchmarkSpendPostTime(client, account)

sdk.internal.set('spendPostTime', spendPostTime)
sdk.internal.set('spendPostTimeAt', Date.now())
await sdk.internal.save()
}
sdk.internal.set('spendPostTime', spendPostTime)
sdk.internal.set('spendPostTimeAt', Date.now())
await sdk.internal.save()
}

return spendPostTime
return spendPostTime
} catch (e) {
// if benchmarking fails, return 0. The consumer of this function should not show an estimate
return 0
}
}

async function benchmarkSpendPostTime(client: RpcClient, account: string): Promise<number> {
Expand All @@ -50,6 +55,11 @@ async function benchmarkSpendPostTime(client: RpcClient, account: string): Promi

const notes = await fetchNotes(client, account, 10)

// Not enough notes in the account to measure the time to combine a note
if (notes.length < 3) {
return 0
}

CliUx.ux.action.start('Measuring time to combine 1 note')

const feeRates = await client.wallet.estimateFeeRates()
Expand Down
14 changes: 13 additions & 1 deletion ironfish-cli/src/utils/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ export class TransactionTimer {
private timer: NodeJS.Timer | undefined

constructor(spendPostTime: number, raw: RawTransaction) {
this.estimateInMs = Math.max(Math.ceil(spendPostTime * raw.spends.length), 1000)
// if spendPostTime is 0, there means that there was an issue measuring the spendPostTime
// we will not show the progress bar in this case

this.estimateInMs =
spendPostTime > 0 ? Math.max(Math.ceil(spendPostTime * raw.spends.length), 1000) : -1
}

getEstimateInMs(): number {
Expand All @@ -46,6 +50,10 @@ export class TransactionTimer {
}

start() {
if (this.estimateInMs <= 0) {
return
}

this.progressBar = CliUx.ux.progress({
format: '{title}: [{bar}] {percentage}% | {estimate}',
}) as ProgressBar
Expand All @@ -72,6 +80,10 @@ export class TransactionTimer {
}

end() {
if (this.estimateInMs <= 0) {
return
}

if (!this.progressBar || !this.startTime || !this.timer) {
return
}
Expand Down
3 changes: 3 additions & 0 deletions ironfish/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
module.exports = {
extends: ['ironfish'],
parserOptions: {
Expand Down

0 comments on commit 0713fb3

Please sign in to comment.