Skip to content

Commit

Permalink
Merge pull request #2599 from iron-fish/staging
Browse files Browse the repository at this point in the history
STAGING -> MASTER v53
  • Loading branch information
NullSoldier authored Nov 19, 2022
2 parents b74cb89 + 359038e commit de27e51
Show file tree
Hide file tree
Showing 29 changed files with 1,186 additions and 433 deletions.
4 changes: 2 additions & 2 deletions ironfish-cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ironfish",
"version": "0.1.52",
"version": "0.1.53",
"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 @@ -56,7 +56,7 @@
"dependencies": {
"@aws-sdk/client-s3": "3.127.0",
"@ironfish/rust-nodejs": "0.1.17",
"@ironfish/sdk": "0.0.29",
"@ironfish/sdk": "0.0.30",
"@oclif/core": "1.16.1",
"@oclif/plugin-help": "5.1.12",
"@oclif/plugin-not-found": "2.3.1",
Expand Down
15 changes: 0 additions & 15 deletions ironfish-cli/src/commands/accounts/pay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@ export class Pay extends IronfishCommand {
}

if (flags.amount) {
if (!CurrencyUtils.isValidIron(flags.amount)) {
this.error(`A valid amount is required`)
}

amount = CurrencyUtils.decodeIron(flags.amount)
}

Expand All @@ -94,17 +90,10 @@ export class Pay extends IronfishCommand {
},
)) as string

if (!CurrencyUtils.isValidIron(input)) {
this.error(`A valid amount is required`)
}

amount = CurrencyUtils.decodeIron(input)
}

if (flags.fee) {
if (!CurrencyUtils.isValidIron(flags.fee)) {
this.error(`A valid fee is required`)
}
fee = CurrencyUtils.decodeIron(flags.fee)
}

Expand Down Expand Up @@ -175,10 +164,6 @@ export class Pay extends IronfishCommand {
},
)) as string

if (!CurrencyUtils.isValidIron(input)) {
this.error(`A valid amount is required`)
}

fee = CurrencyUtils.decodeIron(input)
}

Expand Down
192 changes: 192 additions & 0 deletions ironfish-cli/src/commands/accounts/repair.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/* 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 {
Account,
Assert,
Blockchain,
CurrencyUtils,
NodeUtils,
Wallet,
WalletDB,
} from '@ironfish/sdk'
import { IronfishCommand } from '../../command'
import { LocalFlags } from '../../flags'

const RESCAN_MESSAGE = 'Account must be rescanned using `accounts:rescan --reset`.'
export default class Repair extends IronfishCommand {
static hidden = false

static description = `Repairs wallet database stores`

static flags = {
...LocalFlags,
}

static args = [
{
name: 'account',
parse: (input: string): Promise<string> => Promise.resolve(input.trim()),
required: false,
description: 'Name of the account to repair the database for',
},
]

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

const node = await this.sdk.node()
await NodeUtils.waitForOpen(node)

const account = this.loadAccount(node.wallet, args.account)

this.log(`Repairing wallet for account ${account.name}`)

this.log('Repairing balance')
await this.repairBalance(account, node.wallet.walletDb, node.chain)

this.log('Repairing nullifierToNote')
await this.repairNullifierToNoteHash(account, node.wallet.walletDb)

this.log('Repairing sequenceToNoteHash')
await this.repairSequenceToNoteHash(account, node.wallet.walletDb)
}

private loadAccount(wallet: Wallet, accountName: string | undefined): Account {
if (accountName) {
const account = wallet.getAccountByName(accountName)
if (account) {
return account
}
throw new Error(`No account with name ${accountName}`)
}

const defaultAccount = wallet.getDefaultAccount()
if (defaultAccount) {
return defaultAccount
}

throw new Error('Could not find an account to repair.')
}

private async repairBalance(
account: Account,
walletDb: WalletDB,
chain: Blockchain,
): Promise<void> {
let unconfirmedBalance = 0n

let noteUnspentMismatches = 0

for await (const decryptedNoteValue of account.getNotes()) {
const transactionValue = await account.getTransaction(decryptedNoteValue.transactionHash)

Assert.isNotUndefined(
transactionValue,
`Account has a note but is missing the transaction that it received the note from. ${RESCAN_MESSAGE}`,
)

if (!decryptedNoteValue.nullifier) {
if (transactionValue.sequence) {
throw new Error(
`Transaction marked as on chain, but note missing nullifier. ${RESCAN_MESSAGE}`,
)
}

continue
}

const spent = await chain.nullifiers.contains(decryptedNoteValue.nullifier)

if (spent && !decryptedNoteValue.spent) {
noteUnspentMismatches++

await walletDb.saveDecryptedNote(account, decryptedNoteValue.hash, {
...decryptedNoteValue,
spent: true,
})
} else if (
!spent &&
!chain.verifier.isExpiredSequence(
transactionValue.transaction.expirationSequence(),
chain.head.sequence,
)
) {
if (decryptedNoteValue.spent) {
await walletDb.saveDecryptedNote(account, decryptedNoteValue.hash, {
...decryptedNoteValue,
spent: false,
})
}

unconfirmedBalance += decryptedNoteValue.note.value()
}
}

this.log(
`\tSaving new unconfirmed balance: ${CurrencyUtils.renderIron(unconfirmedBalance, true)}`,
)
await walletDb.saveUnconfirmedBalance(account, unconfirmedBalance)

this.log(
`\tRepaired ${noteUnspentMismatches} decrypted notes incorrectly marked as unspent`,
)
}

private async repairNullifierToNoteHash(account: Account, walletDb: WalletDB): Promise<void> {
let missingNotes = 0

for await (const [[, nullifier], noteHash] of walletDb.nullifierToNoteHash.getAllIter(
undefined,
account.prefixRange,
)) {
const decryptedNoteValue = await account.getDecryptedNote(noteHash)

if (!decryptedNoteValue || !decryptedNoteValue.nullifier) {
missingNotes++

await walletDb.deleteNullifier(account, nullifier)
}
}

this.log(
`\tRepaired ${missingNotes} nullifiers that map to notes that are not in the wallet`,
)
}

private async repairSequenceToNoteHash(account: Account, walletDb: WalletDB): Promise<void> {
let incorrectSequences = 0

for await (const [, [sequence, noteHash]] of walletDb.sequenceToNoteHash.getAllKeysIter(
undefined,
account.prefixRange,
)) {
const decryptedNoteValue = await account.getDecryptedNote(noteHash)

if (!decryptedNoteValue) {
incorrectSequences++

await walletDb.sequenceToNoteHash.del([account.prefix, [sequence, noteHash]])

continue
}

const transactionValue = await account.getTransaction(decryptedNoteValue.transactionHash)

Assert.isNotUndefined(
transactionValue,
`Account has a note but is missing the transaction that it received the note from. ${RESCAN_MESSAGE}`,
)

if (transactionValue.sequence !== sequence) {
incorrectSequences++

await walletDb.sequenceToNoteHash.del([account.prefix, [sequence, noteHash]])

await walletDb.setNoteHashSequence(account, noteHash, transactionValue.sequence)
}
}

this.log(`\tRepaired ${incorrectSequences} incorrect sequenceToNoteHash mappings`)
}
}
2 changes: 1 addition & 1 deletion ironfish-cli/src/commands/config/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class EditCommand extends IronfishCommand {
const output = JSON.stringify(response.content, undefined, ' ')

const tmpDir = os.tmpdir()
const folderPath = await mkdtempAsync(path.join(tmpDir, '@ironfish/sdk'))
const folderPath = await mkdtempAsync(path.join(tmpDir, '@ironfish-sdk-'))
const filePath = path.join(folderPath, DEFAULT_CONFIG_NAME)

await writeFileAsync(filePath, output)
Expand Down
2 changes: 1 addition & 1 deletion ironfish/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ironfish/sdk",
"version": "0.0.29",
"version": "0.0.30",
"description": "SDK for running and interacting with an Iron Fish node",
"author": "Iron Fish <[email protected]> (https://ironfish.network)",
"main": "build/src/index.js",
Expand Down
70 changes: 70 additions & 0 deletions ironfish/src/blockchain/__fixtures__/blockchain.test.ts.fixture
Original file line number Diff line number Diff line change
Expand Up @@ -1868,5 +1868,75 @@
}
]
}
],
"Blockchain does not grant mining reward after V3_DISABLE_MINING_REWARD": [
{
"id": "29b775ff-ead1-4bd2-b7aa-af92714922ff",
"name": "test",
"spendingKey": "e513465f9213ba0ed31b9737bb6b342cbc954a3484b5e179906a0fd58c5e3325",
"incomingViewKey": "ff4f5e9ac1ec57def4ea77eab0717759277dfaae2cf40821eceba0ed4a8e2407",
"outgoingViewKey": "c92029f3aac586ab97429a57d832717a6d8912de3e2448835b71a822e988cf6c",
"publicAddress": "36a995ab772edf57b8fac3b79a8aae9edd5dfdd60ef82cf035db2a0873b53f4bcbdd5778ca130298b96337"
},
{
"header": {
"sequence": 2,
"previousBlockHash": "69E263E931FA1A2A4B0437A8EFF79FFB7A353B6384A7AEAC9F90AC12AE4811EF",
"noteCommitment": {
"commitment": {
"type": "Buffer",
"data": "base64:P8rqlkk2exaMxGoe5AmkK2+cj6iv+sUsJNg1bXvc+SM="
},
"size": 4
},
"nullifierCommitment": {
"commitment": "E2484D0BF38F29EFFD63EF9D5A61202F198129862B12845182A4CA77AA557A4B",
"size": 1
},
"target": "12167378078913471945996581698193578003257923478462384564023044230",
"randomness": "0",
"timestamp": 1668725306390,
"minersFee": "-2000000000",
"work": "0",
"hash": "6B85750A385456C0B98D544B20BB90D03286E711714B602983E95FE59E11EC29",
"graffiti": "0000000000000000000000000000000000000000000000000000000000000000"
},
"transactions": [
{
"type": "Buffer",
"data": "base64:AAAAAAAAAAABAAAAAAAAAABsyoj/////AAAAALCa6bInu5vjZdq5Sr0RbOPKfcC3M8vruHIrQEty2HXNxuSROOz8KVeXNBO+qLvr5bcjOHgV/zG2iIXIyHsGZKS00gYJQHlm1TzuT5E4G1A17FMhmtMItZKqa7xY8QBfPgAwZWRtNZURpYeAyIN467q3QOiBvMJy7NXmu9W/4B+SliUBxGswE5jJI8sQMJ8f5oiM3aViGvPipN9L1gvi1g2FIahOz8n9Fx8KUpHg/DBnN8QCDJAbGisxCTuQl0swkFlviJ9hv8p97ehr6Srqgry0j5s1aYT6uuhFnEOOQGfmfReVsxBdwfc6Fq5lCPYNheZhZ3lOmAvLr3aUDSd4lAV120YnuxaG0SGszC5+OKpvHEymPf/eiio90ud/8kD4xK6BCeQdwFtaBdaO1GKvMn1EmtBBldyntyzq0JlnlXQ74wtpNoSr0DcCo9NtGi4vt5vqJVnE/3niFMDazMz1PRAlPya6OVb/YGFqc9DeUaaaWnGgeXvveh25PzJiUH6bRjwUs0JlYW5zdGFsayBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwEOMcafnHkIPx21WcopObHhDUByhVMvO0zVHsjC8W7DGq2veWvoptfMw1kpFWwngiiv4MazjqQFEacO1vn2FzCw=="
}
]
},
{
"header": {
"sequence": 3,
"previousBlockHash": "6B85750A385456C0B98D544B20BB90D03286E711714B602983E95FE59E11EC29",
"noteCommitment": {
"commitment": {
"type": "Buffer",
"data": "base64:CF6fn1dy7aLLNRzMdTji9hZ2HVKQynk6jrWJC/3Jmm8="
},
"size": 5
},
"nullifierCommitment": {
"commitment": "E2484D0BF38F29EFFD63EF9D5A61202F198129862B12845182A4CA77AA557A4B",
"size": 1
},
"target": "12131835591833296355903882315508391652467087441833704656133504637",
"randomness": "0",
"timestamp": 1668725306564,
"minersFee": "0",
"work": "0",
"hash": "975E845A21543AC75161D09386D2B36696029D4C10BF52E8CDF9D52B994C0D72",
"graffiti": "0000000000000000000000000000000000000000000000000000000000000000"
},
"transactions": [
{
"type": "Buffer",
"data": "base64:AAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAILkVVqXNUC92/giUIjQt7oT3vgm6bu6/ubb3yqqCj8LmZlP5ymkFLnTIXYGjJfelKLnimgKFFhyKbWw/e3ut6L2+Mes6HIZdVrcxLPj4ZzU7yKC2F4xvOGOGvbmJe8jXAlHv4FdVDwcaQb7jls8+oi2Ae8kobFhfAvH2Xdhz6d+R1/F8EfQjMjW18So+NHMgKi06ehmB6FGbXkpfDd5R8N/HwnAqKqUoGdVl7MsTCiyut5XHSxAuFanU8WAECMVfutHRLvTx6TlnNrdjaZNwxdm5XZjEfnfAUrWSWiODG+LLHVuhw4ye4aKpEhdOVHm2uZlad1ONlKlj61TCn4vClYZkkb3BvKoF6VZx8pEdRDwju8Byze00CAkaziCTkCyuG+JUqbuC04oDENG3d4PW6KNCRcgRXlbFFzJLqB7kJ3F9V0q2rFXyrOnKdLM4EYFPul/Rl2QHHM0gITfyK8RKb1k+0XbFbxStncUe/zPF9Er+eUMJw6FxtDDRBAn/RA9HxDDq0JlYW5zdGFsayBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwGpURf4Kdm8EgH1jtft5tPQQtEkCxp7cMK3O75ewIVDL0RIvjl6DUP5d3TOy48aR/ipEjyZS14quSkF+drhL9CQ=="
}
]
}
]
}
20 changes: 20 additions & 0 deletions ironfish/src/blockchain/blockchain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1002,4 +1002,24 @@ describe('Blockchain', () => {
isFork: false,
})
})

it('does not grant mining reward after V3_DISABLE_MINING_REWARD', async () => {
const { node } = await nodeTest.createSetup()
node.chain.consensus.V3_DISABLE_MINING_REWARD = 3
const account = await useAccountFixture(node.wallet)

const block1 = await useMinerBlockFixture(node.chain, 2, account)
expect(block1.minersFee.fee()).toEqual(-20n * 10n ** 8n)
expect(block1.minersFee.spendsLength()).toEqual(0)
expect(block1.minersFee.notesLength()).toEqual(1)
await expect(node.chain).toAddBlock(block1)

const block2 = await useMinerBlockFixture(node.chain, 3, account)
expect(block2.minersFee.fee()).toEqual(0n)
expect(block2.minersFee.spendsLength()).toEqual(0)
expect(block2.minersFee.notesLength()).toEqual(1)
await expect(node.chain).toAddBlock(block2)

await node.wallet.updateHead()
})
})
Loading

0 comments on commit de27e51

Please sign in to comment.