Skip to content

Commit

Permalink
Merge pull request #2581 from iron-fish/staging
Browse files Browse the repository at this point in the history
STAGING -> MASTER
  • Loading branch information
hughy authored Nov 16, 2022
2 parents 6d57e38 + 0851645 commit 1bf0c8e
Show file tree
Hide file tree
Showing 124 changed files with 5,760 additions and 3,666 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/rust_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ jobs:
with:
shared-key: base

- name: Check that cargo lockfile is up to date
uses: actions-rs/cargo@v1
with:
command: check
args: --locked

# Build & Run test & Collect Code coverage
- name: Run cargo-tarpaulin on ironfish-rust
uses: actions-rs/[email protected]
Expand Down
18 changes: 18 additions & 0 deletions Cargo.lock

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

10 changes: 9 additions & 1 deletion config/eslint-config-ironfish/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,19 @@ module.exports = {
'@typescript-eslint/no-unsafe-assignment': 'off',
// It's common to want to mock unbound methods.
'@typescript-eslint/unbound-method': 'off',
// Using try catch with expect.assertsions(n) is the recommended way to
// Using try catch with expect.assertions(n) is the recommended way to
// test async code where you need a reference to the error to validate the
// type and properties
'jest/no-conditional-expect': 'off',
'jest/no-try-expect': 'off',
// It's common to want to compare non-primitive types using expect
// statements grouped in a helper function.
'jest/expect-expect': [
'error',
{
'assertFunctionNames': ['expect*'],
}
],
},
},
],
Expand Down
6 changes: 3 additions & 3 deletions ironfish-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ironfish",
"version": "0.1.51",
"version": "0.1.52",
"description": "CLI for running and interacting with an Iron Fish node",
"author": "Iron Fish <[email protected]> (https://ironfish.network)",
"main": "build/src/index.js",
Expand Down Expand Up @@ -55,8 +55,8 @@
},
"dependencies": {
"@aws-sdk/client-s3": "3.127.0",
"@ironfish/rust-nodejs": "0.1.16",
"@ironfish/sdk": "0.0.28",
"@ironfish/rust-nodejs": "0.1.17",
"@ironfish/sdk": "0.0.29",
"@oclif/core": "1.16.1",
"@oclif/plugin-help": "5.1.12",
"@oclif/plugin-not-found": "2.3.1",
Expand Down
36 changes: 13 additions & 23 deletions ironfish-cli/src/commands/accounts/balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ export class BalanceCommand extends IronfishCommand {
required: false,
description: 'Minimum number of blocks confirmations for a note',
}),
ore: Flags.boolean({
default: false,
description: 'Show amounts in ore',
}),
}

static args = [
Expand All @@ -54,29 +50,23 @@ export class BalanceCommand extends IronfishCommand {
})

if (flags.explain) {
this.explainBalance(response.content, flags.ore)
this.explainBalance(response.content)
return
}

if (flags.all) {
this.log(`Account: ${response.content.account}`)
this.log(
`Balance: ${CurrencyUtils.render(response.content.confirmed, true, flags.ore)}`,
)
this.log(
`Unconfirmed: ${CurrencyUtils.render(response.content.unconfirmed, true, flags.ore)}`,
)
this.log(
`Pending: ${CurrencyUtils.render(response.content.pending, true, flags.ore)}`,
)
this.log(`Balance: ${CurrencyUtils.renderIron(response.content.confirmed, true)}`)
this.log(`Unconfirmed: ${CurrencyUtils.renderIron(response.content.unconfirmed, true)}`)
this.log(`Pending: ${CurrencyUtils.renderIron(response.content.pending, true)}`)
return
}

this.log(`Account: ${response.content.account}`)
this.log(`Balance: ${CurrencyUtils.render(response.content.confirmed, true, flags.ore)}`)
this.log(`Balance: ${CurrencyUtils.renderIron(response.content.confirmed, true)}`)
}

explainBalance(response: GetBalanceResponse, ore: boolean): void {
explainBalance(response: GetBalanceResponse): void {
const unconfirmed = CurrencyUtils.decode(response.unconfirmed)
const pending = CurrencyUtils.decode(response.pending)
const confirmed = CurrencyUtils.decode(response.confirmed)
Expand All @@ -88,24 +78,24 @@ export class BalanceCommand extends IronfishCommand {
this.log('')

this.log(`Your balance is made of notes on the chain that are safe to spend`)
this.log(`Balance: ${CurrencyUtils.render(confirmed, true, ore)}`)
this.log(`Balance: ${CurrencyUtils.renderIron(confirmed, true)}`)
this.log('')

this.log(
`${response.unconfirmedCount} notes worth ${CurrencyUtils.render(
`${response.unconfirmedCount} notes worth ${CurrencyUtils.renderIron(
unconfirmedDelta,
ore,
true,
)} are on the chain within ${response.minimumBlockConfirmations.toString()} blocks of the head`,
)
this.log(`Unconfirmed: ${CurrencyUtils.render(unconfirmed, ore)}`)
this.log(`Unconfirmed: ${CurrencyUtils.renderIron(unconfirmed, true)}`)
this.log('')

this.log(
`${response.pendingCount} notes worth ${CurrencyUtils.render(
`${response.pendingCount} notes worth ${CurrencyUtils.renderIron(
pendingDelta,
ore,
true,
)} are waiting for miners to add them to the chain`,
)
this.log(`Pending: ${CurrencyUtils.render(pending, ore)}`)
this.log(`Pending: ${CurrencyUtils.renderIron(pending, true)}`)
}
}
6 changes: 5 additions & 1 deletion ironfish-cli/src/commands/accounts/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,17 @@ export class TransactionCommand extends IronfishCommand {
this.log(`---Notes---\n`)

CliUx.ux.table(response.content.transaction.notes, {
owner: {
header: 'Owner',
get: (note) => (note.owner ? `✔` : `x`),
},
amount: {
header: 'Amount ($IRON)',
get: (note) => CurrencyUtils.renderIron(note.value),
},
isSpent: {
header: 'Spent',
get: (note) => (note.spent ? `✔` : `x`),
get: (note) => (!note.owner ? '?' : note.spent ? `✔` : `x`),
},
memo: {
header: 'Memo',
Expand Down
186 changes: 186 additions & 0 deletions ironfish-cli/src/commands/chain/hardfork.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/* 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 {
Assert,
Block,
GENESIS_BLOCK_SEQUENCE,
IronfishNode,
Meter,
NodeUtils,
TimeUtils,
} from '@ironfish/sdk'
import { CliUx, Flags } from '@oclif/core'
import { IronfishCommand } from '../../command'
import { LocalFlags } from '../../flags'
import { ProgressBar } from '../../types'
import { rewindChainTo } from './rewind'

const HARD_FORK_HASH = '00000000000006ce61057e714ede8471d15cc9d19f0ff58eee179cadf3ba1f31'
const HARD_FORK_SEQUENCE = 270446

export default class RepairHardFork extends IronfishCommand {
static description = 'Repairs your blockchain in the case that you are on hardfork 270446'

static args = [
{
name: 'start',
required: false,
description: 'The block sequence to start repairing at',
},
]

static flags = {
...LocalFlags,
dry: Flags.boolean({
default: false,
description: 'Dry run repair first',
}),
batchSize: Flags.integer({
default: 1000,
description: 'How many blocks to load in parallel',
}),
}

async start(): Promise<void> {
const { args, flags } = await this.parse(RepairHardFork)

const start = args.start ? Number(args.start) : GENESIS_BLOCK_SEQUENCE

const node = await this.sdk.node()

await NodeUtils.waitForOpen(node)
await this.rewindChain(node, flags.dry)
await this.repairNullifiers(node, start, flags.dry, flags.batchSize)
}

async repairNullifiers(
node: IronfishNode,
start: number,
dryRun: boolean,
batchSize: number,
): Promise<void> {
let current = start
const stop = node.chain.head.sequence

const header = await node.chain.getHeaderAtSequence(start)
Assert.isNotNull(header)
const block = await node.chain.getBlock(header)
Assert.isNotNull(block)

const spendCount = Array.from(block.spends()).length
let nullifierTreeIndex = header.nullifierCommitment.size - spendCount
let nullifierRepaired = 0

const processBatch = async (blocks: Block[]): Promise<void> => {
for (const block of blocks) {
for (const spend of block.spends()) {
const contains = await node.chain.nullifiers.contains(spend.nullifier)

if (!contains) {
nullifierRepaired++

this.log(
`\rMissing nullifier: block ${
block.header.sequence
}, index: ${nullifierTreeIndex}: ${spend.nullifier.toString(
'hex',
)} (repaired ${nullifierRepaired})${''.padEnd(10, ' ')}`,
)

refreshProgressBar()

const existing = await node.chain.nullifiers.getLeaf(nullifierTreeIndex)
const merkleHash = node.chain.nullifiers.hasher.merkleHash(spend.nullifier)
Assert.isTrue(merkleHash.equals(existing.merkleHash))

if (!dryRun) {
await node.chain.nullifiers.leavesIndex.put(merkleHash, nullifierTreeIndex)
}
}

speedNullifiers.add(1)
nullifierTreeIndex++
}
}
}

const progressBar = CliUx.ux.progress({
format:
'Repairing blockchain: [{bar}] {percentage}% | {value} / {total} blocks | {speed}/bps | {speedNullifiers}/nps | ETA: {estimate}',
}) as ProgressBar

progressBar.start(stop, current, {
speed: '0',
speedNullifiers: '0',
estimate: TimeUtils.renderEstimate(0, 0, 0),
})

const refreshProgressBar = () => {
progressBar.update(current, {
speed: speed.rate1m.toFixed(0),
speedNullifiers: speedNullifiers.rate1m.toFixed(0),
estimate: TimeUtils.renderEstimate(current, stop, speed.rate1m),
})
}

const speedNullifiers = new Meter()
speedNullifiers.start()

const speed = new Meter()
speed.start()

const batch = new Array<Promise<Block>>()

for (let i = current; i <= node.chain.head.sequence; ++i) {
const promise = node.chain.getHeaderAtSequence(i).then(async (header) => {
Assert.isNotNull(header)
const block = await node.chain.getBlock(header)
Assert.isNotNull(block)
return block
})

batch.push(promise)

if (batch.length > batchSize) {
const blocks = await Promise.all(batch)
await processBatch(blocks)

speed.add(batch.length)
current += batch.length
batch.length = 0

refreshProgressBar()
}
}

const blocks = await Promise.all(batch)
await processBatch(blocks)
speed.add(batch.length)
current += batch.length
batch.length = 0

speed.stop()
speedNullifiers.stop()
progressBar.stop()
}

async rewindChain(node: IronfishNode, dryRun: boolean): Promise<void> {
const header = await node.chain.getHeaderAtSequence(HARD_FORK_SEQUENCE)
if (!header) {
return
}

const hasHardFork = header.hash.toString('hex') === HARD_FORK_HASH
if (!hasHardFork) {
return
}

this.log(`Your node has a known hard fork at sequence ${HARD_FORK_SEQUENCE}`)
this.log(`Rewinding your blockchain to before the hard fork.`)

if (!dryRun) {
await rewindChainTo(this, node, HARD_FORK_SEQUENCE - 1)
}
}
}
2 changes: 2 additions & 0 deletions ironfish-cli/src/commands/chain/repair.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const SPEED_ESTIMATE = 42
export default class RepairChain extends IronfishCommand {
static description = 'Rebuild the main chain to fix corruption'

static hidden = true

static flags = {
...LocalFlags,
confirm: Flags.boolean({
Expand Down
Loading

0 comments on commit 1bf0c8e

Please sign in to comment.