From 8cceacefe902ab9d944deeb6030134aeade5ea15 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Mon, 24 Jun 2024 21:33:19 -0700 Subject: [PATCH 01/81] Have WalletScanner use chain directly if available (#5063) (#5066) This PR changes it so if you're running the wallet inside of the node, instead of iterating the chain using the RPC system it'll use the chain directly. This is a large performance optimziation when scaanning the blockchain. Co-authored-by: Andrea --- ironfish/src/node.ts | 1 + .../scanner/chainProcessorWithTransactions.ts | 45 ++++++++++++++++ ironfish/src/wallet/scanner/walletScanner.ts | 53 ++++++++++++++----- ironfish/src/wallet/wallet.test.ts | 2 +- ironfish/src/wallet/wallet.ts | 7 ++- 5 files changed, 91 insertions(+), 17 deletions(-) create mode 100644 ironfish/src/wallet/scanner/chainProcessorWithTransactions.ts diff --git a/ironfish/src/node.ts b/ironfish/src/node.ts index afa33e502a..bdd70abd05 100644 --- a/ironfish/src/node.ts +++ b/ironfish/src/node.ts @@ -327,6 +327,7 @@ export class FullNode { networkId: network.id, nodeClient: memoryClient, logger, + chain, }) const node = new FullNode({ diff --git a/ironfish/src/wallet/scanner/chainProcessorWithTransactions.ts b/ironfish/src/wallet/scanner/chainProcessorWithTransactions.ts new file mode 100644 index 0000000000..4e7033291d --- /dev/null +++ b/ironfish/src/wallet/scanner/chainProcessorWithTransactions.ts @@ -0,0 +1,45 @@ +/* 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 } from '../../assert' +import { Blockchain } from '../../blockchain' +import { ChainProcessor } from '../../chainProcessor' +import { Event } from '../../event' +import { Logger } from '../../logger' +import { BlockHeader, Transaction } from '../../primitives' + +export class ChainProcessorWithTransactions { + chainProcessor: ChainProcessor + onAdd = new Event<[{ header: BlockHeader; transactions: Transaction[] }]>() + onRemove = new Event<[{ header: BlockHeader; transactions: Transaction[] }]>() + + get hash(): Buffer | null { + return this.chainProcessor.hash + } + set hash(value: Buffer | null) { + this.chainProcessor.hash = value + } + + async update(options?: { signal?: AbortSignal }): Promise<{ hashChanged: boolean }> { + return this.chainProcessor.update(options) + } + + constructor(options: { logger?: Logger; chain: Blockchain; head: Buffer | null }) { + this.chainProcessor = new ChainProcessor(options) + + this.chainProcessor.onAdd.on(async (header: BlockHeader) => { + const block = await this.chainProcessor.chain.getBlock(header) + Assert.isNotNull(block) + const transactions = block.transactions + await this.onAdd.emitAsync({ header, transactions }) + }) + + this.chainProcessor.onRemove.on(async (header: BlockHeader) => { + const block = await this.chainProcessor.chain.getBlock(header) + Assert.isNotNull(block) + const transactions = block.transactions + await this.onRemove.emitAsync({ header, transactions }) + }) + } +} diff --git a/ironfish/src/wallet/scanner/walletScanner.ts b/ironfish/src/wallet/scanner/walletScanner.ts index 89a2793ce3..4b5f774dcd 100644 --- a/ironfish/src/wallet/scanner/walletScanner.ts +++ b/ironfish/src/wallet/scanner/walletScanner.ts @@ -1,7 +1,8 @@ /* 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 type { Blockchain } from '../../blockchain' +import type { RpcClient } from '../../rpc' import type { Wallet } from '../wallet' import { BufferMap } from 'buffer-map' import { Assert } from '../../assert' @@ -9,32 +10,36 @@ import { Config } from '../../fileStores' import { Logger } from '../../logger' import { Mutex } from '../../mutex' import { BlockHeader, Transaction } from '../../primitives' -import { RpcClient } from '../../rpc' import { AsyncUtils, BufferUtils, HashUtils } from '../../utils' import { DecryptedNote } from '../../workerPool/tasks/decryptNotes' +import { HeadValue } from '../walletdb/headValue' +import { ChainProcessorWithTransactions } from './chainProcessorWithTransactions' import { RemoteChainProcessor } from './remoteChainProcessor' import { ScanState } from './scanState' export class WalletScanner { readonly logger: Logger - readonly nodeClient: RpcClient | null readonly wallet: Wallet + readonly config: Config + + readonly chain: Blockchain | null = null + readonly nodeClient: RpcClient | null = null - maxQueueSize: number state: ScanState | null = null lock: Mutex = new Mutex() constructor(options: { logger: Logger - nodeClient: RpcClient | null wallet: Wallet - maxQueueSize: number config: Config + nodeClient?: RpcClient | null + chain?: Blockchain | null }) { this.logger = options.logger - this.nodeClient = options.nodeClient this.wallet = options.wallet - this.maxQueueSize = options.maxQueueSize + this.config = options.config + this.chain = options.chain ?? null + this.nodeClient = options.nodeClient ?? null } get running(): boolean { @@ -64,12 +69,7 @@ export class WalletScanner { const start = await this.wallet.getEarliestHead() const end = await this.wallet.getChainHead() - const chainProcessor = new RemoteChainProcessor({ - logger: this.logger, - nodeClient: this.nodeClient, - maxQueueSize: this.maxQueueSize, - head: start?.hash ?? null, - }) + const chainProcessor = this.getChainProcessor(start) chainProcessor.onAdd.on(async ({ header, transactions }) => { await this.connectBlock(header, transactions, this.state?.abortController) @@ -226,6 +226,31 @@ export class WalletScanner { await this.wallet.disconnectBlockForAccount(account, header, transactions) } } + + getChainProcessor( + start: HeadValue | null, + ): ChainProcessorWithTransactions | RemoteChainProcessor { + const head = start?.hash ?? null + + if (this.chain) { + return new ChainProcessorWithTransactions({ + logger: this.logger, + chain: this.chain, + head, + }) + } + + if (this.nodeClient) { + return new RemoteChainProcessor({ + logger: this.logger, + nodeClient: this.nodeClient, + maxQueueSize: this.config.get('walletSyncingMaxQueueSize'), + head, + }) + } + + throw new Error('WalletScanner requires either chain or client') + } } function getTransactionsWithNoteIndex( diff --git a/ironfish/src/wallet/wallet.test.ts b/ironfish/src/wallet/wallet.test.ts index ad79899bf9..a09906db41 100644 --- a/ironfish/src/wallet/wallet.test.ts +++ b/ironfish/src/wallet/wallet.test.ts @@ -385,7 +385,7 @@ describe('Wallet', () => { expect(head?.hash).toEqualHash(node.chain.genesis.hash) // set max syncing queue to 1 so that wallet only fetches one block at a time - node.wallet.scanner.maxQueueSize = 1 + node.wallet.scanner.config.set('walletSyncingMaxQueueSize', 1) await node.wallet.scan() diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index c2c2d44f4b..8fce590faf 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.ts @@ -1,6 +1,7 @@ /* 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 type { Blockchain } from '../blockchain' import { Asset, generateKey, @@ -120,6 +121,7 @@ export class Wallet { consensus, networkId, nodeClient, + chain, }: { config: Config database: WalletDB @@ -129,6 +131,7 @@ export class Wallet { consensus: Consensus networkId: number nodeClient: RpcClient | null + chain: Blockchain | null }) { this.config = config this.logger = logger.withTag('accounts') @@ -144,9 +147,9 @@ export class Wallet { this.scanner = new WalletScanner({ wallet: this, logger: this.logger, + config: this.config, nodeClient: this.nodeClient, - maxQueueSize: this.config.get('walletSyncingMaxQueueSize'), - config: config, + chain: chain, }) } From 30bba2fde1463334e78913a1994b0b415f1e22e0 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Mon, 24 Jun 2024 22:00:24 -0700 Subject: [PATCH 02/81] Introduce optional pagination to ChainProcessor (#5068) * Introduce optional pagination to ChainProcessor * Added test --- .../chainProcessor.test.ts.fixture | 54 +++++++++++++++++++ ironfish/src/chainProcessor.test.ts | 32 +++++++++++ ironfish/src/chainProcessor.ts | 23 +++++++- .../scanner/chainProcessorWithTransactions.ts | 7 ++- ironfish/src/wallet/scanner/walletScanner.ts | 1 + 5 files changed, 114 insertions(+), 3 deletions(-) diff --git a/ironfish/src/__fixtures__/chainProcessor.test.ts.fixture b/ironfish/src/__fixtures__/chainProcessor.test.ts.fixture index 40b3968114..e3f9fb4665 100644 --- a/ironfish/src/__fixtures__/chainProcessor.test.ts.fixture +++ b/ironfish/src/__fixtures__/chainProcessor.test.ts.fixture @@ -238,5 +238,59 @@ } ] } + ], + "ChainProcessor limits blocks processed with maxQueueSize": [ + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:9EZ7vYfAuHAUuprnDU5VAVe9dxWtuZpD+inRrqQTrhw=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:8T95/ZdJe2TfXl2p79vhgZ/fDaoyso4zy6/dleJ1RaU=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1719290394961, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA/0pQVkKqklXaDXMzjwmClRCoxt1PDoOdCIu9GfN/J9uyFwCLx48dsvjyrVdfykTQQi37Q3PEeYDz5Ztx3ishxXWnBPrnAsLeDHQWR8hw1cGujkVLIfNARZBCRCFLpD1Ta6zDxVU5Dax7dgorcL6KBUNcS8KfOk05GgQ91Wxhz5oQAtbCpbeoA6IlUlT7Jb2zYjbJIvSpfFLxd2GQEwEmfJHpQQ4LhrJfvfeutKFtP2WACNZlQ9YjuYGxjJk4uKTX2VmX+RTICZGDQj8t73jm9li0Zjrm4t+/SVgKhXANMr3gLA9PPlNjafqExtvH/VabPyb7G7G7CyCEa9qJQsCrtC+oJn6jSMQzDZ16vizio5v+ZMr4n5YlJn7Rc8ZgIR9RLGqHv9KIa8t+WDVlrsij/hYvI93l9EnEU5bmVvYY93AdxtKu5hIvL17EJlVvyaYPCPudVAXi9HdWOoAaob+uMs5KSGFx7HrnyceQo/sNpgfhvzhm4tMN9xk/kr4wa5JioILsCMQODPVQ5vHHyRSOPft3+e2lvP4wXoaW7Js4k8OcIrDR+jaLzgizllOw18cjg/wn0Fub8e+bNT+j9YjrHH7ZhLx91MA+utfFxx1AiqKhxrxgxrdAfklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJYpWultwIyXPG98GGM/qMZHsG4otZYHF2IQvcqNVdCQhHFekvVrPNbE8n9/AHsLX4k3gDSNsSUxH2hAYRFlpBA==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "152059029BF9A7BD3FE699F609CF8F01616F801DD33137F7D9DDE47D26C4EEB7", + "noteCommitment": { + "type": "Buffer", + "data": "base64:hE/okdJVaojx51iv+VKf7b8Y1ikMcPWpapwytQ0x/QA=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:iZKfZUm5q84TbDyrOWyXhVK7j4qtTlYLQRxTrQB/uI0=" + }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1719290395283, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAi5H+T86c4kJHlqb9KkEFjGm+WMMYQeP2EhRN3vW/O7OKKwxL1+mwZ1DtpSVs1rho5XOzh0HGRLqVE2GDz548eljHOnpnGbd+7K7gB+xrUBOsK+AtmjkaCWtOgetIus+3ZTfZDAOCIrIaBsB1LU+27gw7Hgb5irlMdUgjH6ATxO4SAruKlIORsIXNAptMbJycLIdrhLW0fwn+ivtCEHu7Tgg1GxemCbO/kLnUjAJwCQ2PW9EzBefl9q4UKg+ZdqfYTGY5pSiIJIN5BtkdXZJXmBUs2IPxFdZt14KtNrwQYm3YW+grSGEgG8mOJ+Goro+5kau1AGzK/tM/44+xp4e5SCeDe4FyGOz8N2i+hPxw/8qByNmKz0dub+wbXTVmil0BmnN0I2LOFX6LqfXH/S1zpKumHYkPsslsZv/E9Sm+MbE95V2erwmy9XW6VAdMW+20zi9V9Oj7ufgPE28xegmOQljOSmVb+qkTTWLOaor1ickSjGKwbMqB+rRx16vhKVBwipiJ2csfCbqYfdvK31+mhmgy7S8Xti03DrmKtMjMns5hRopgy0O76FevFmPJOgLV6qJ1ZmY3eNjhrqgItDnTYwYkO3NbezqXHHVDKjXgAwo2n2gbMojgUElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw4kkz3LRo7ckLiJruENNs5TdwWmk3l1Rg1fT+MWU6j0Hvl58fDt604px/q2hBQV054q6A26cUjNJGJ288qHXeAA==" + } + ] + } ] } \ No newline at end of file diff --git a/ironfish/src/chainProcessor.test.ts b/ironfish/src/chainProcessor.test.ts index 08cdb903ff..adfcd485cc 100644 --- a/ironfish/src/chainProcessor.test.ts +++ b/ironfish/src/chainProcessor.test.ts @@ -138,4 +138,36 @@ describe('ChainProcessor', () => { expect(result.hashChanged).toEqual(false) expect(processor.hash).toEqual(chain.genesis.hash) }) + + it('limits blocks processed with maxQueueSize', async () => { + const { chain } = nodeTest + + const block1 = await useMinerBlockFixture(chain) + await expect(chain).toAddBlock(block1) + const block2 = await useMinerBlockFixture(chain) + await expect(chain).toAddBlock(block2) + + expect(chain.head.hash).toEqual(block2.header.hash) + + const onEvent: jest.Mock = jest.fn() + + const processor = new ChainProcessor({ chain, head: null }) + processor.onAdd.on((block) => onEvent(block, 'add')) + processor.onRemove.on((block) => onEvent(block, 'remove')) + + processor.hash = chain.genesis.hash + processor.maxQueueSize = 1 + await processor.update() + expect(onEvent).toHaveBeenCalledTimes(1) + expect(onEvent).toHaveBeenNthCalledWith(1, block1.header, 'add') + + onEvent.mockReset() + + processor.hash = chain.genesis.hash + processor.maxQueueSize = null + await processor.update() + expect(onEvent).toHaveBeenCalledTimes(2) + expect(onEvent).toHaveBeenNthCalledWith(1, block1.header, 'add') + expect(onEvent).toHaveBeenNthCalledWith(2, block2.header, 'add') + }) }) diff --git a/ironfish/src/chainProcessor.ts b/ironfish/src/chainProcessor.ts index c5888c18ea..74389403eb 100644 --- a/ironfish/src/chainProcessor.ts +++ b/ironfish/src/chainProcessor.ts @@ -35,11 +35,18 @@ export class ChainProcessor { logger: Logger onAdd = new Event<[block: BlockHeader]>() onRemove = new Event<[block: BlockHeader]>() - - constructor(options: { logger?: Logger; chain: Blockchain; head: Buffer | null }) { + maxQueueSize: number | null + + constructor(options: { + logger?: Logger + chain: Blockchain + head: Buffer | null + maxQueueSize?: number | null + }) { this.chain = options.chain this.logger = (options.logger ?? createRootLogger()).withTag('chainprocessor') this.hash = options.head + this.maxQueueSize = options.maxQueueSize ?? null } private async add(header: BlockHeader): Promise { @@ -73,6 +80,8 @@ export class ChainProcessor { `Chain processor head not found in chain: ${this.hash.toString('hex')}`, ) + let blockCount = 0 + const fork = await this.chain.findFork(head, chainHead) // All cases can be handled by rewinding to the fork point @@ -92,6 +101,11 @@ export class ChainProcessor { await this.remove(remove) this.hash = remove.previousBlockHash this.sequence = remove.sequence - 1 + + blockCount++ + if (this.maxQueueSize && blockCount >= this.maxQueueSize) { + return { hashChanged: !oldHash || !this.hash.equals(oldHash) } + } } const iterForwards = this.chain.iterateTo(fork, chainHead, undefined, false) @@ -108,6 +122,11 @@ export class ChainProcessor { await this.add(add) this.hash = add.hash this.sequence = add.sequence + + blockCount++ + if (this.maxQueueSize && blockCount >= this.maxQueueSize) { + return { hashChanged: !oldHash || !this.hash.equals(oldHash) } + } } return { hashChanged: !oldHash || !this.hash.equals(oldHash) } diff --git a/ironfish/src/wallet/scanner/chainProcessorWithTransactions.ts b/ironfish/src/wallet/scanner/chainProcessorWithTransactions.ts index 4e7033291d..52ee2ff997 100644 --- a/ironfish/src/wallet/scanner/chainProcessorWithTransactions.ts +++ b/ironfish/src/wallet/scanner/chainProcessorWithTransactions.ts @@ -25,7 +25,12 @@ export class ChainProcessorWithTransactions { return this.chainProcessor.update(options) } - constructor(options: { logger?: Logger; chain: Blockchain; head: Buffer | null }) { + constructor(options: { + logger?: Logger + chain: Blockchain + head: Buffer | null + maxQueueSize?: number | null + }) { this.chainProcessor = new ChainProcessor(options) this.chainProcessor.onAdd.on(async (header: BlockHeader) => { diff --git a/ironfish/src/wallet/scanner/walletScanner.ts b/ironfish/src/wallet/scanner/walletScanner.ts index 4b5f774dcd..a679adbb09 100644 --- a/ironfish/src/wallet/scanner/walletScanner.ts +++ b/ironfish/src/wallet/scanner/walletScanner.ts @@ -236,6 +236,7 @@ export class WalletScanner { return new ChainProcessorWithTransactions({ logger: this.logger, chain: this.chain, + maxQueueSize: this.config.get('walletSyncingMaxQueueSize'), head, }) } From 00c8efddd436d77ed6cf3711afe64e5d68265e81 Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Tue, 25 Jun 2024 12:54:17 -0700 Subject: [PATCH 03/81] Update remaining artifact github actions to v4 (#5072) --- .github/workflows/deploy-npm-ironfish-rust-nodejs.yml | 2 +- .github/workflows/publish-binaries.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-npm-ironfish-rust-nodejs.yml b/.github/workflows/deploy-npm-ironfish-rust-nodejs.yml index 652fe6a8ac..9eedb960db 100644 --- a/.github/workflows/deploy-npm-ironfish-rust-nodejs.yml +++ b/.github/workflows/deploy-npm-ironfish-rust-nodejs.yml @@ -34,7 +34,7 @@ jobs: run: npm install --no-workspaces - name: Download all artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: path: ironfish-rust-nodejs/artifacts diff --git a/.github/workflows/publish-binaries.yml b/.github/workflows/publish-binaries.yml index f59f418c6b..6cd4559df8 100644 --- a/.github/workflows/publish-binaries.yml +++ b/.github/workflows/publish-binaries.yml @@ -51,7 +51,7 @@ jobs: echo "Runner architecture does not match specified architecture" exit 1 fi - + # needed for distutils, which is used by nodegyp, arm64 mac runners have 3.12 - name: Set up Python uses: actions/setup-python@v3 @@ -193,7 +193,7 @@ jobs: xcrun notarytool submit "${{ steps.set_paths.outputs.zip }}" --keychain-profile "notarytool-profile" --wait - name: Upload artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ steps.set_paths.outputs.name }} path: tools/${{ steps.set_paths.outputs.zip }} From 57d21eddc7977b4999a84857621f4328d80ffb3c Mon Sep 17 00:00:00 2001 From: andrea Date: Mon, 24 Jun 2024 17:17:01 -0700 Subject: [PATCH 04/81] Update our fork of `jubjub` to the latest version This version brings in optional elliptic curve point multiplication stats. Not enabled by default but useful for performance analysis. --- Cargo.lock | 2 +- supply-chain/audits.toml | 3 ++- supply-chain/config.toml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 690a9abf22..4f20bfdd9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1586,7 +1586,7 @@ dependencies = [ [[package]] name = "jubjub" version = "0.9.0" -source = "git+https://github.com/iron-fish/jubjub.git?branch=blstrs#a1a0c2ed69eec4d5d5e87842e2a40849f7fa4633" +source = "git+https://github.com/iron-fish/jubjub.git?branch=blstrs#6fac532975e23acd3fa670bc0383d027f09403e5" dependencies = [ "bitvec", "blst", diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 916d3dd977..4f1ab3c20f 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -63,7 +63,8 @@ delta = "1.9.3 -> 2.2.6" [[audits.jubjub]] who = "Andrea " criteria = "safe-to-deploy" -delta = "0.9.0 -> 0.9.0@git:a1a0c2ed69eec4d5d5e87842e2a40849f7fa4633" +delta = "0.9.0 -> 0.9.0@git:6fac532975e23acd3fa670bc0383d027f09403e5" +importable = false notes = "Fork of the official jubjub owned by Iron Fish" [[audits.mio]] diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 1d70d75448..8dad55873b 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -37,7 +37,7 @@ audit-as-crates-io = true [policy.ironfish_zkp] audit-as-crates-io = true -[policy."jubjub:0.9.0@git:a1a0c2ed69eec4d5d5e87842e2a40849f7fa4633"] +[policy."jubjub:0.9.0@git:6fac532975e23acd3fa670bc0383d027f09403e5"] audit-as-crates-io = true [policy."reddsa:0.5.1@git:311baf8865f6e21527d1f20750d8f2cf5c9e531a"] From d4d2e1af7a64c7f9478f22c41c0dab398773c5c7 Mon Sep 17 00:00:00 2001 From: andrea Date: Mon, 24 Jun 2024 17:17:50 -0700 Subject: [PATCH 05/81] Prune the `cargo vet` exemptions --- supply-chain/config.toml | 4 ---- supply-chain/imports.lock | 6 ++++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/supply-chain/config.toml b/supply-chain/config.toml index 8dad55873b..af8d1e6797 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -182,10 +182,6 @@ criteria = "safe-to-deploy" version = "0.2.4" criteria = "safe-to-deploy" -[[exemptions.cobs]] -version = "0.2.3" -criteria = "safe-to-deploy" - [[exemptions.const-crc32]] version = "1.2.0" criteria = "safe-to-deploy" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index b989759feb..b9e91188b0 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -133,6 +133,12 @@ who = "Pat Hickey " criteria = "safe-to-deploy" version = "0.2.0" +[[audits.bytecode-alliance.audits.cobs]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.2.3" +notes = "No `unsafe` code in the crate and no usage of `std`" + [[audits.bytecode-alliance.audits.constant_time_eq]] who = "Nick Fitzgerald " criteria = "safe-to-deploy" From 96457b0dbbf0367ca52dc1c4e329403304ad7912 Mon Sep 17 00:00:00 2001 From: andrea Date: Mon, 24 Jun 2024 21:41:20 -0700 Subject: [PATCH 06/81] Add a development-only feature to print statistics related to notes encryption This commit adds an optional feature (that can be enabled only during local development) to print the number of cryptographic operations performed for note decryption and encryption. This feature is enabled by building `ironfish-rust-nodejs` through `yarn build:stats`. After doing that, and after starting `ironfish-cli` through `yarn start `, it is possible to trigger the printing of the statistics by sending `SIGUSR1` to the CLI process. This commit has no impact on release builds of Iron Fish. --- Cargo.lock | 20 +++++++ ironfish-rust-nodejs/Cargo.toml | 4 +- ironfish-rust-nodejs/package.json | 1 + ironfish-rust-nodejs/src/lib.rs | 3 + ironfish-rust-nodejs/src/stats.rs | 89 ++++++++++++++++++++++++++++++ ironfish-rust/Cargo.toml | 1 + ironfish-rust/src/merkle_note.rs | 91 +++++++++++++++++++++++++++++++ supply-chain/audits.toml | 10 ++++ supply-chain/imports.lock | 5 ++ 9 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 ironfish-rust-nodejs/src/stats.rs diff --git a/Cargo.lock b/Cargo.lock index 4f20bfdd9b..d1411f1663 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1517,6 +1517,7 @@ dependencies = [ "napi-derive", "num_cpus", "rand", + "signal-hook", ] [[package]] @@ -2510,6 +2511,25 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "2.0.0" diff --git a/ironfish-rust-nodejs/Cargo.toml b/ironfish-rust-nodejs/Cargo.toml index f69fa5ab97..d1c153def1 100644 --- a/ironfish-rust-nodejs/Cargo.toml +++ b/ironfish-rust-nodejs/Cargo.toml @@ -23,7 +23,8 @@ workspace = true [lib] crate-type = ["cdylib"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +stats = ["ironfish/note-encryption-stats", "jubjub/stats", "dep:signal-hook"] [dependencies] base64 = "0.13.0" @@ -35,6 +36,7 @@ napi-derive = "2.13.0" jubjub = { git = "https://github.com/iron-fish/jubjub.git", branch = "blstrs" } rand = "0.8.5" num_cpus = "1.16.0" +signal-hook = { version = "0.3.17", optional = true, default-features = false, features = ["iterator"] } [build-dependencies] napi-build = "2.0.1" diff --git a/ironfish-rust-nodejs/package.json b/ironfish-rust-nodejs/package.json index 7a4ca058ba..3b75843e3b 100644 --- a/ironfish-rust-nodejs/package.json +++ b/ironfish-rust-nodejs/package.json @@ -14,6 +14,7 @@ "artifacts": "napi artifacts", "build": "yarn clean && napi build --platform --release", "build:debug": "napi build --platform", + "build:stats": "yarn clean && napi build --platform --release --features=stats", "prepublishOnly": "napi prepublish --skip-gh-release", "test:slow": "jest --testPathIgnorePatterns --testMatch \"**/*.test.slow.ts\"", "clean": "rimraf target" diff --git a/ironfish-rust-nodejs/src/lib.rs b/ironfish-rust-nodejs/src/lib.rs index 46694fa68b..60d2aa4f54 100644 --- a/ironfish-rust-nodejs/src/lib.rs +++ b/ironfish-rust-nodejs/src/lib.rs @@ -24,6 +24,9 @@ pub mod rolling_filter; pub mod signal_catcher; pub mod structs; +#[cfg(feature = "stats")] +pub mod stats; + fn to_napi_err(err: impl Display) -> napi::Error { Error::from_reason(err.to_string()) } diff --git a/ironfish-rust-nodejs/src/stats.rs b/ironfish-rust-nodejs/src/stats.rs new file mode 100644 index 0000000000..82a4b03719 --- /dev/null +++ b/ironfish-rust-nodejs/src/stats.rs @@ -0,0 +1,89 @@ +/* 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/. */ + +#![cfg(feature = "stats")] + +use napi::JsObject; +use napi_derive::module_exports; +use signal_hook::{consts::signal::SIGUSR1, iterator::Signals}; +use std::fmt::Write as FmtWrite; +use std::io::{self, Write as IoWrite}; +use std::sync::Once; +use std::thread; + +fn print_stats(colors: bool) { + let ecpm_stats = jubjub::stats(); + let note_stats = ironfish::merkle_note::stats::get(); + + // Write the stats in a buffer first, then write the buffer to stderr. The goal of the buffer + // is to attempt to write the stats in a single syscall, so that the stats output will not be + // interleaved with the rest of the output from the main process. This may not always work + // (depending on the buffering performed by stderr), but it is better than calling `print!` + // directly. + let mut s = String::new(); + + write!( + &mut s, + "\n\ + {highlight}Elliptic Curve Point Multiplication Stats:\n\ + • affine muls: {affine_muls}\n\ + • extended muls: {extended_muls}\n\ + Note Encryption Stats:\n\ + • total: {note_construct}\n\ + Note Decryption Stats:\n\ + • for owner: {note_dec_for_owner} ({note_dec_for_owner_ok} [{note_dec_for_owner_ok_percent:.2}%] successful)\n\ + • for spender: {note_dec_for_spender} ({note_dec_for_spender_ok} [{note_dec_for_spender_ok_percent:.2}%] successful){reset}\n\ + \n", + highlight = if colors { "\x1b[1;31m" } else { "" }, + reset = if colors { "\x1b[0m" } else { "" }, + affine_muls = ecpm_stats.affine_muls, + extended_muls = ecpm_stats.extended_muls, + note_construct = note_stats.construct, + note_dec_for_owner = note_stats.decrypt_note_for_owner.total, + note_dec_for_owner_ok = note_stats.decrypt_note_for_owner.successful, + note_dec_for_owner_ok_percent = + note_stats.decrypt_note_for_owner.successful as f64 / + note_stats.decrypt_note_for_owner.total as f64 * 100., + note_dec_for_spender = note_stats.decrypt_note_for_spender.total, + note_dec_for_spender_ok = note_stats.decrypt_note_for_spender.successful, + note_dec_for_spender_ok_percent = + note_stats.decrypt_note_for_spender.successful as f64 / + note_stats.decrypt_note_for_spender.total as f64 * 100., + ).expect("failed to write stats to buffer"); + + let mut stderr = io::stderr().lock(); + stderr + .write_all(s.as_bytes()) + .and_then(|()| stderr.flush()) + .expect("failed to write stats to stderr"); +} + +/// Prints statistics whenever SIGUSR1 is sent to this process. +fn print_stats_on_signal() { + let mut signals = Signals::new([SIGUSR1]).expect("failed to set up signal handler"); + for _ in signals.forever() { + print_stats(true); + } +} + +pub fn setup_signal_handler() { + thread::spawn(print_stats_on_signal); +} + +/// Sets up the stats signal handler when the N-API module is imported. +/// +/// Technically, the `module_exports` macro is supposed to be used on code that sets up the +/// JavaScript exports. Here we are abusing the functionality a little bit by performing other side +/// actions. However, given that all the code contained in this module is for development only and +/// will never be released to the public, we consider this use acceptable. +#[module_exports] +fn init(_exports: JsObject) -> napi::Result<()> { + // Due to how Node worker threads work, this N-API module will be imported multiple times (once + // for the main thread, then once for each worker thread spawned). We use `Once` here to avoid + // setting up multiple signal handlers, which would result in duplicate stats messages being + // emitted after receiving a signal. + static START: Once = Once::new(); + START.call_once(setup_signal_handler); + Ok(()) +} diff --git a/ironfish-rust/Cargo.toml b/ironfish-rust/Cargo.toml index 593487bf41..e3969bccbf 100644 --- a/ironfish-rust/Cargo.toml +++ b/ironfish-rust/Cargo.toml @@ -26,6 +26,7 @@ workspace = true [features] benchmark = [] download-params = ["dep:reqwest"] +note-encryption-stats = [] [lib] name = "ironfish" diff --git a/ironfish-rust/src/merkle_note.rs b/ironfish-rust/src/merkle_note.rs index 7607f75b92..8067e5d297 100644 --- a/ironfish-rust/src/merkle_note.rs +++ b/ironfish-rust/src/merkle_note.rs @@ -128,6 +128,9 @@ impl MerkleNote { diffie_hellman_keys: &EphemeralKeyPair, note_encryption_keys: [u8; NOTE_ENCRYPTION_KEY_SIZE], ) -> MerkleNote { + #[cfg(feature = "note-encryption-stats")] + stats::inc_construct(); + let secret_key = diffie_hellman_keys.secret(); let public_key = diffie_hellman_keys.public(); @@ -180,10 +183,16 @@ impl MerkleNote { &self, owner_view_key: &IncomingViewKey, ) -> Result { + #[cfg(feature = "note-encryption-stats")] + stats::inc_decrypt_note_for_owner(); + let shared_secret = owner_view_key.shared_secret(&self.ephemeral_public_key); let note = Note::from_owner_encrypted(owner_view_key, &shared_secret, &self.encrypted_note)?; note.verify_commitment(self.note_commitment)?; + + #[cfg(feature = "note-encryption-stats")] + stats::inc_decrypt_note_for_owner_ok(); Ok(note) } @@ -191,6 +200,9 @@ impl MerkleNote { &self, spender_key: &OutgoingViewKey, ) -> Result { + #[cfg(feature = "note-encryption-stats")] + stats::inc_decrypt_note_for_spender(); + let encryption_key = calculate_key_for_encryption_keys( spender_key, &self.value_commitment, @@ -206,6 +218,9 @@ impl MerkleNote { let note = Note::from_spender_encrypted(public_address.0, &shared_key, &self.encrypted_note)?; note.verify_commitment(self.note_commitment)?; + + #[cfg(feature = "note-encryption-stats")] + stats::inc_decrypt_note_for_spender_ok(); Ok(note) } } @@ -269,6 +284,82 @@ fn calculate_key_for_encryption_keys( .expect("has has incorrect length") } +#[cfg(feature = "note-encryption-stats")] +pub mod stats { + use std::sync::atomic::AtomicUsize; + use std::sync::atomic::Ordering::Relaxed; + + static CONSTRUCT_CALLS: AtomicUsize = AtomicUsize::new(0); + + static DECRYPT_FOR_OWNER_CALLS: AtomicUsize = AtomicUsize::new(0); + static DECRYPT_FOR_OWNER_OK_CALLS: AtomicUsize = AtomicUsize::new(0); + + static DECRYPT_FOR_SPENDER_CALLS: AtomicUsize = AtomicUsize::new(0); + static DECRYPT_FOR_SPENDER_OK_CALLS: AtomicUsize = AtomicUsize::new(0); + + /// Statistics on the operations performed by the [`ironfish::merkle_note`] module. + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub struct Stats { + /// Number of [`MerkleNote::construct()`] calls made so far. + pub construct: usize, + /// Number of [`MerkleNote::decrypt_note_for_owner()`] calls made so far. + pub decrypt_note_for_owner: CallResultStats, + /// Number of [`MerkleNote::decrypt_note_for_spender()`] calls made so far. + pub decrypt_note_for_spender: CallResultStats, + } + + /// Statistics for a function that returns a `Result`. + #[derive(Copy, Clone, PartialEq, Eq, Debug)] + pub struct CallResultStats { + /// Number of calls to a function performed so far. This includes both successful (`Ok`) + /// and unsuccessful (`Err`) calls. + pub total: usize, + /// Number of calls to a function performed so far that returned a successful (`Ok`) + /// result. + pub successful: usize, + } + + #[inline(always)] + pub(super) fn inc_construct() { + CONSTRUCT_CALLS.fetch_add(1, Relaxed); + } + + #[inline(always)] + pub(super) fn inc_decrypt_note_for_owner() { + DECRYPT_FOR_OWNER_CALLS.fetch_add(1, Relaxed); + } + + #[inline(always)] + pub(super) fn inc_decrypt_note_for_owner_ok() { + DECRYPT_FOR_OWNER_OK_CALLS.fetch_add(1, Relaxed); + } + + #[inline(always)] + pub(super) fn inc_decrypt_note_for_spender() { + DECRYPT_FOR_SPENDER_CALLS.fetch_add(1, Relaxed); + } + + #[inline(always)] + pub(super) fn inc_decrypt_note_for_spender_ok() { + DECRYPT_FOR_SPENDER_OK_CALLS.fetch_add(1, Relaxed); + } + + #[must_use] + pub fn get() -> Stats { + Stats { + construct: CONSTRUCT_CALLS.load(Relaxed), + decrypt_note_for_owner: CallResultStats { + total: DECRYPT_FOR_OWNER_CALLS.load(Relaxed), + successful: DECRYPT_FOR_OWNER_OK_CALLS.load(Relaxed), + }, + decrypt_note_for_spender: CallResultStats { + total: DECRYPT_FOR_SPENDER_CALLS.load(Relaxed), + successful: DECRYPT_FOR_SPENDER_OK_CALLS.load(Relaxed), + }, + } + } +} + #[cfg(test)] mod test { use super::MerkleNote; diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 4f1ab3c20f..a025a80c4e 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -89,6 +89,16 @@ delta = "0.5.1 -> 0.5.1@git:311baf8865f6e21527d1f20750d8f2cf5c9e531a" importable = false notes = "Unreleased changes required by ironfish-frost to support multisig wallets" +[[audits.signal-hook]] +who = "andrea " +criteria = "safe-to-deploy" +version = "0.3.17" + +[[audits.signal-hook-registry]] +who = "andrea " +criteria = "safe-to-deploy" +delta = "1.4.1 -> 1.4.2" + [[audits.siphasher]] who = "Andrea " criteria = "safe-to-deploy" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index b9e91188b0..f27eb30550 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -304,6 +304,11 @@ criteria = "safe-to-deploy" version = "1.0.17" notes = "plenty of unsafe pointer and vec tricks, but in well-structured and commented code that appears to be correct" +[[audits.bytecode-alliance.audits.signal-hook-registry]] +who = "Pat Hickey " +criteria = "safe-to-deploy" +version = "1.4.1" + [[audits.bytecode-alliance.audits.slab]] who = "Pat Hickey " criteria = "safe-to-deploy" From 04fddc5c3e65240eba87a03b4a9808c89434f78d Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Tue, 25 Jun 2024 13:46:02 -0700 Subject: [PATCH 07/81] Simplify ChainProcessor return logic (#5073) * Simplify ChainProcessor return logic We only ever cared if work was done, not that the specific hash was changed from the original. * Add test to ensure chain remains stable --- .../chainProcessor.test.ts.fixture | 28 +++++++++++++++++++ ironfish/src/chainProcessor.test.ts | 27 ++++++++++++++++++ ironfish/src/chainProcessor.ts | 19 +++++++------ 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/ironfish/src/__fixtures__/chainProcessor.test.ts.fixture b/ironfish/src/__fixtures__/chainProcessor.test.ts.fixture index e3f9fb4665..c870be92ff 100644 --- a/ironfish/src/__fixtures__/chainProcessor.test.ts.fixture +++ b/ironfish/src/__fixtures__/chainProcessor.test.ts.fixture @@ -292,5 +292,33 @@ } ] } + ], + "ChainProcessor should remain stable if hash is head": [ + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:DHVQe7C8CrO0PQZrmqpIxHOyLQ/s/kqcMd/SzDSE4ks=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:BsNJJNbVpE4ABdLMBD0o/FQ9JT/kOctfk16EtQLu878=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1719347456625, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAdsfrXOYvO2c+rICPEMxgSLNBscHmG1y5ml5fPjnjS6CYLq3OUb2AYi/gYXO0s6RIK1iIRIZvjC0ckGS93XmCmZqGhWPIPd664zIe3oO272iz/wTnBdq0yBvDuV9nSTBSk7LLVSsEZgj+M7p1H/HuHHZY6lGJNANRUKXNuyBGBOIFVHeJABxe8HvrGdxviove8KY++mSrMzJTN4OIFv1XUbAJSUUWRMMBFKaZxy0papGpCp8LQ7wur7mQO1/5M+W93RFpwqZn87LnVSPOqG3ns5y7zl2jsQJDNJ1bTbjqm5TFEA7ZLZQUj6qsTMHZpLb0mblNEX/wo5jPPKzSxCAtQrUMjAk8AgSYChpr+lyU1t+Siq0dZ/eRB158YsPHtWJNgSFu/bVZXT5woVjzHdZO8f1z3DpPK1OkMq5OiKqC8INKpWWTo09Mkse2XTwWSRP5uZuh9/2+yTQySYEfRqcP0j3sFxo2M3bmAXArWlFP7EdITysrEMzkLPiVwAD5edM3/hkw4lsws4f2VeVe9WUCIVSRjztOB1s5jE5+6CI+/y1v04/2c5lLlYTBzsSYF/dHYOSpIHHGGpE8tuOZoJMMlbmYGNFmRy/KCxCjfuzgDwwMXKAtW9K5R0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw9U2y863G+KW6lppD3d7rHCmm60FWDhKO99ASS8BSsHHgA0Wgp16gyaLFfs/r3DhRCCuvb1keyzpTDAiMShraDQ==" + } + ] + } ] } \ No newline at end of file diff --git a/ironfish/src/chainProcessor.test.ts b/ironfish/src/chainProcessor.test.ts index adfcd485cc..5b230a7b7c 100644 --- a/ironfish/src/chainProcessor.test.ts +++ b/ironfish/src/chainProcessor.test.ts @@ -170,4 +170,31 @@ describe('ChainProcessor', () => { expect(onEvent).toHaveBeenNthCalledWith(1, block1.header, 'add') expect(onEvent).toHaveBeenNthCalledWith(2, block2.header, 'add') }) + + it('should remain stable if hash is head', async () => { + const { chain } = nodeTest + + const block = await useMinerBlockFixture(chain) + await expect(chain).toAddBlock(block) + expect(chain.head.hash).toEqual(block.header.hash) + + const onEvent: jest.Mock = jest.fn() + + const processor = new ChainProcessor({ chain, head: chain.genesis.hash }) + processor.onAdd.on((block) => onEvent(block, 'add')) + processor.onRemove.on((block) => onEvent(block, 'remove')) + + let result = await processor.update() + expect(result.hashChanged).toBe(true) + expect(processor.hash).toEqualBuffer(block.header.hash) + expect(onEvent).toHaveBeenCalledTimes(1) + expect(onEvent).toHaveBeenNthCalledWith(1, block.header, 'add') + + onEvent.mockReset() + + result = await processor.update() + expect(result.hashChanged).toBe(false) + expect(processor.hash).toEqualBuffer(block.header.hash) + expect(onEvent).toHaveBeenCalledTimes(0) + }) }) diff --git a/ironfish/src/chainProcessor.ts b/ironfish/src/chainProcessor.ts index 74389403eb..5958c39ad9 100644 --- a/ironfish/src/chainProcessor.ts +++ b/ironfish/src/chainProcessor.ts @@ -58,8 +58,6 @@ export class ChainProcessor { } async update({ signal }: { signal?: AbortSignal } = {}): Promise<{ hashChanged: boolean }> { - const oldHash = this.hash - if (!this.hash) { await this.add(this.chain.genesis) this.hash = this.chain.genesis.hash @@ -81,6 +79,7 @@ export class ChainProcessor { ) let blockCount = 0 + let hashChanged = false const fork = await this.chain.findFork(head, chainHead) @@ -91,7 +90,7 @@ export class ChainProcessor { for await (const remove of iterBackwards) { if (signal?.aborted) { - return { hashChanged: !oldHash || !this.hash.equals(oldHash) } + return { hashChanged } } if (remove.hash.equals(fork.hash)) { @@ -101,10 +100,11 @@ export class ChainProcessor { await this.remove(remove) this.hash = remove.previousBlockHash this.sequence = remove.sequence - 1 - + hashChanged = true blockCount++ + if (this.maxQueueSize && blockCount >= this.maxQueueSize) { - return { hashChanged: !oldHash || !this.hash.equals(oldHash) } + return { hashChanged } } } @@ -112,7 +112,7 @@ export class ChainProcessor { for await (const add of iterForwards) { if (signal?.aborted) { - return { hashChanged: !oldHash || !this.hash.equals(oldHash) } + return { hashChanged } } if (add.hash.equals(fork.hash)) { @@ -122,13 +122,14 @@ export class ChainProcessor { await this.add(add) this.hash = add.hash this.sequence = add.sequence - + hashChanged = true blockCount++ + if (this.maxQueueSize && blockCount >= this.maxQueueSize) { - return { hashChanged: !oldHash || !this.hash.equals(oldHash) } + return { hashChanged } } } - return { hashChanged: !oldHash || !this.hash.equals(oldHash) } + return { hashChanged } } } From 45ef180f358e21da6dd3593841cd6d2d528abd35 Mon Sep 17 00:00:00 2001 From: Hugh Cunningham <57735705+hughy@users.noreply.github.com> Date: Tue, 25 Jun 2024 15:17:46 -0700 Subject: [PATCH 08/81] fixes dkg round3 prompt for secret package (#5076) makes it explicit that this should be the round 2 secret package --- ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts b/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts index 29f1ef8d45..4a667bb46a 100644 --- a/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts +++ b/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts @@ -52,7 +52,7 @@ export class DkgRound3Command extends IronfishCommand { let round2SecretPackage = flags.round2SecretPackage if (!round2SecretPackage) { round2SecretPackage = await ux.prompt( - `Enter the encrypted secret package for participant ${participantName}`, + `Enter the round 2 encrypted secret package for participant ${participantName}`, { required: true, }, From a2e2e17fb420631790f03d4482cde8865ffd347c Mon Sep 17 00:00:00 2001 From: andrea Date: Tue, 25 Jun 2024 19:36:47 -0700 Subject: [PATCH 09/81] Suppress logs during nodeless wallet rescans MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running `wallet:rescan` when not connected to a node produces output like the following: ``` Scanning blocks: [░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0% | 60 / 624032 | 0.00 / sec | ETA: N/AAdded block seq: 100, hash: 00000...bd9a1 Scanning blocks: [░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0% | 128 / 624032 | 0.00 / sec | ETA: N/AAdded block seq: 200, hash: 00000...c61b7 Scanning blocks: [░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0% | 282 / 624032 | 0.00 / sec | ETA: N/AAdded block seq: 300, hash: 00000...194db ``` That is: the progress bar output is disrupted by the scanner logs. This commit suppresses the logs coming from the wallet so that the progress bar is the only output displayed. --- ironfish-cli/src/commands/wallet/rescan.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ironfish-cli/src/commands/wallet/rescan.ts b/ironfish-cli/src/commands/wallet/rescan.ts index 06fea5f00f..9467347fd5 100644 --- a/ironfish-cli/src/commands/wallet/rescan.ts +++ b/ironfish-cli/src/commands/wallet/rescan.ts @@ -2,6 +2,7 @@ * 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 { setLogLevelFromConfig } from '@ironfish/sdk' import { Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' @@ -39,6 +40,12 @@ export class RescanCommand extends IronfishCommand { stdout: true, }) + // Suppress log messages from the wallet scanner, to prevent those messages + // from interfering with the progress bar. This problem can occur only if + // not connected to a remote node (i.e. we're running with the in-memory + // rpc). + setLogLevelFromConfig('wallet:error') + const response = client.wallet.rescan({ follow }) const progress = new ProgressBar('Scanning blocks', { From 1f27bdbd23409ef42f961244eabcba80cc63e75e Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Thu, 27 Jun 2024 14:47:26 -0700 Subject: [PATCH 10/81] add ability to create unsigned transaction for mint cli command (#5081) --- ironfish-cli/src/commands/wallet/mint.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ironfish-cli/src/commands/wallet/mint.ts b/ironfish-cli/src/commands/wallet/mint.ts index a97139eb89..2b5025c236 100644 --- a/ironfish-cli/src/commands/wallet/mint.ts +++ b/ironfish-cli/src/commands/wallet/mint.ts @@ -106,6 +106,12 @@ export class Mint extends IronfishCommand { description: 'The public address of the account to transfer ownership of this asset to.', required: false, }), + unsignedTransaction: Flags.boolean({ + default: false, + description: + 'Return a serialized UnsignedTransaction. Use it to create a transaction and build proofs but not post to the network', + exclusive: ['rawTransaction'], + }), } async start(): Promise { @@ -234,7 +240,7 @@ export class Mint extends IronfishCommand { } let expiration = flags.expiration - if (flags.rawTransaction && expiration === undefined) { + if ((flags.rawTransaction || flags.unsignedTransaction) && expiration === undefined) { expiration = await promptExpiration({ logger: this.logger, client: client }) } @@ -284,6 +290,16 @@ export class Mint extends IronfishCommand { this.exit(0) } + if (flags.unsignedTransaction) { + const response = await client.wallet.buildTransaction({ + account, + rawTransaction: RawTransactionSerde.serialize(raw).toString('hex'), + }) + this.log('Unsigned Transaction') + this.log(response.content.unsignedTransaction) + this.exit(0) + } + await this.confirm( account, amount, From 64e57378b16798d72080ebe02626a4f078a18148 Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Thu, 27 Jun 2024 15:43:48 -0700 Subject: [PATCH 11/81] Add unsigned transaction flag to `wallet:burn` (#5088) --- ironfish-cli/src/commands/wallet/burn.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ironfish-cli/src/commands/wallet/burn.ts b/ironfish-cli/src/commands/wallet/burn.ts index d61a3fd368..cc751e368c 100644 --- a/ironfish-cli/src/commands/wallet/burn.ts +++ b/ironfish-cli/src/commands/wallet/burn.ts @@ -72,6 +72,12 @@ export class Burn extends IronfishCommand { description: 'Return raw transaction. Use it to create a transaction but not post to the network', }), + unsignedTransaction: Flags.boolean({ + default: false, + description: + 'Return a serialized UnsignedTransaction. Use it to create a transaction and build proofs but not post to the network', + exclusive: ['rawTransaction'], + }), expiration: Flags.integer({ char: 'e', description: @@ -173,7 +179,7 @@ export class Burn extends IronfishCommand { } let expiration = flags.expiration - if (flags.rawTransaction && expiration === undefined) { + if ((flags.rawTransaction || flags.unsignedTransaction) && expiration === undefined) { expiration = await promptExpiration({ logger: this.logger, client: client }) } @@ -219,6 +225,16 @@ export class Burn extends IronfishCommand { this.exit(0) } + if (flags.unsignedTransaction) { + const response = await client.wallet.buildTransaction({ + account, + rawTransaction: RawTransactionSerde.serialize(raw).toString('hex'), + }) + this.log('Unsigned Transaction') + this.log(response.content.unsignedTransaction) + this.exit(0) + } + await this.confirm(assetData, amount, raw.fee, account, flags.confirm) ux.action.start('Sending the transaction') From 8059ee92eeadd4796095b281c8513cbfe9bb9e75 Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Thu, 27 Jun 2024 16:53:10 -0700 Subject: [PATCH 12/81] Make unsignedTransaction flag visible in send command. (#5087) * Make unsignedTransaction flag visible in send command. * move display transaction summary above raw and unsigned transaction exits --- ironfish-cli/src/commands/wallet/send.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ironfish-cli/src/commands/wallet/send.ts b/ironfish-cli/src/commands/wallet/send.ts index 6ca3aa60af..54507f4975 100644 --- a/ironfish-cli/src/commands/wallet/send.ts +++ b/ironfish-cli/src/commands/wallet/send.ts @@ -96,7 +96,6 @@ export class Send extends IronfishCommand { 'Return raw transaction. Use it to create a transaction but not post to the network', }), unsignedTransaction: Flags.boolean({ - hidden: true, default: false, description: 'Return a serialized UnsignedTransaction. Use it to create a transaction and build proofs but not post to the network', @@ -255,6 +254,8 @@ export class Send extends IronfishCommand { raw = RawTransactionSerde.deserialize(bytes) } + displayTransactionSummary(raw, assetData, amount, from, to, memo) + if (flags.rawTransaction) { this.log('Raw Transaction') this.log(RawTransactionSerde.serialize(raw).toString('hex')) @@ -272,8 +273,6 @@ export class Send extends IronfishCommand { this.exit(0) } - displayTransactionSummary(raw, assetData, amount, from, to, memo) - const spendPostTime = getSpendPostTimeInMs(this.sdk) const transactionTimer = new TransactionTimer(spendPostTime, raw) From 0f321c1f0bc9b23cf1f6bbe5b27b8726273944d5 Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 27 Jun 2024 14:59:36 -0700 Subject: [PATCH 13/81] DecryptNotes: move the code that groups notes by account from WorkerPool to the response class --- ironfish/src/workerPool/pool.ts | 24 +------------- .../src/workerPool/tasks/decryptNotes.test.ts | 33 +++++++++++++++++++ ironfish/src/workerPool/tasks/decryptNotes.ts | 26 +++++++++++++++ 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/ironfish/src/workerPool/pool.ts b/ironfish/src/workerPool/pool.ts index 39b2071d28..57cabbc965 100644 --- a/ironfish/src/workerPool/pool.ts +++ b/ironfish/src/workerPool/pool.ts @@ -4,7 +4,6 @@ import { getCpuCount, UnsignedTransaction } from '@ironfish/rust-nodejs' import _ from 'lodash' -import { Assert } from '../assert' import { VerificationResult, VerificationResultReason } from '../consensus' import { createRootLogger, Logger } from '../logger' import { Meter, MetricsMonitor } from '../metrics' @@ -205,28 +204,7 @@ export class WorkerPool { throw new Error('Invalid response') } - // The response contains a linear array of notes for efficiency, but we - // need to return a more structured response - - const decryptedNotesByAccount = new Map>() - for (const { accountId } of accountKeys) { - decryptedNotesByAccount.set(accountId, []) - } - - let index = 0 - for (const _ of encryptedNotes) { - for (const { accountId } of accountKeys) { - const nextNote: DecryptedNote | null | undefined = response.notes[index++] - const accountDecryptedNotes = decryptedNotesByAccount.get(accountId) - Assert.isNotUndefined(nextNote) - Assert.isNotUndefined(accountDecryptedNotes) - accountDecryptedNotes.push(nextNote) - } - } - - Assert.isEqual(index, response.notes.length) - - return decryptedNotesByAccount + return response.mapToAccounts(accountKeys) } /** diff --git a/ironfish/src/workerPool/tasks/decryptNotes.test.ts b/ironfish/src/workerPool/tasks/decryptNotes.test.ts index efe1994d53..f3063c9e0f 100644 --- a/ironfish/src/workerPool/tasks/decryptNotes.test.ts +++ b/ironfish/src/workerPool/tasks/decryptNotes.test.ts @@ -101,6 +101,39 @@ describe('DecryptNotesResponse', () => { const deserializedResponse = DecryptNotesResponse.deserializePayload(request.jobId, buffer) expect(deserializedResponse.notes).toHaveLength(length) }) + + describe('mapToAccounts', () => { + it('returns a map linking each account to its notes', () => { + const accounts = 'abcdefghijklmnopqrstuvwxyz' + .split('') + .map((letter) => ({ accountId: letter })) + const notesPerAccount = 100 + const length = accounts.length * notesPerAccount + + const request = new DecryptNotesResponse( + Array.from({ length }, () => ({ + forSpender: false, + index: 1, + hash: Buffer.alloc(32, 1), + nullifier: Buffer.alloc(32, 1), + serializedNote: Buffer.alloc(DECRYPTED_NOTE_LENGTH, 1), + })), + 0, + ) + + const accountsToNotes = request.mapToAccounts(accounts) + expect(accountsToNotes.size).toBe(accounts.length) + + const returnedAccounts = Array.from(accountsToNotes.keys()) + .sort() + .map((accountId) => ({ accountId })) + expect(returnedAccounts).toEqual(accounts) + + for (const notes of accountsToNotes.values()) { + expect(notes.length).toBe(notesPerAccount) + } + }) + }) }) describe('DecryptNotesTask', () => { diff --git a/ironfish/src/workerPool/tasks/decryptNotes.ts b/ironfish/src/workerPool/tasks/decryptNotes.ts index f65365a2c1..70c170d6f4 100644 --- a/ironfish/src/workerPool/tasks/decryptNotes.ts +++ b/ironfish/src/workerPool/tasks/decryptNotes.ts @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { DECRYPTED_NOTE_LENGTH, ENCRYPTED_NOTE_LENGTH } from '@ironfish/rust-nodejs' import bufio from 'bufio' +import { Assert } from '../../assert' import { NoteEncrypted } from '../../primitives/noteEncrypted' import { ACCOUNT_KEY_LENGTH } from '../../wallet' import { VIEW_KEY_LENGTH } from '../../wallet/walletdb/accountValue' @@ -225,6 +226,31 @@ export class DecryptNotesResponse extends WorkerMessage { return size } + + /** + * Groups each note in the response by the account it belongs to. The + * `accounts` passed must be in the same order as the `accountKeys` in the + * `DecryptNotesRequest` that generated this response. + */ + mapToAccounts( + accounts: ReadonlyArray<{ accountId: string }>, + ): Map> { + const decryptedNotesByAccount: Array< + [accountId: string, notes: Array] + > = accounts.map(({ accountId }) => [accountId, []]) + + let noteIndex = 0 + while (noteIndex < this.notes.length) { + for (const [_, accountNotes] of decryptedNotesByAccount) { + const nextNote: DecryptedNote | null | undefined = this.notes[noteIndex++] + Assert.isNotUndefined(nextNote) + accountNotes.push(nextNote) + } + } + + Assert.isEqual(noteIndex, this.notes.length) + return new Map(decryptedNotesByAccount) + } } export class DecryptNotesTask extends WorkerTask { From 5483ee5f9d42fa3c4a0fe0c5d896e6695b9cbe3b Mon Sep 17 00:00:00 2001 From: andrea Date: Fri, 28 Jun 2024 11:18:21 -0700 Subject: [PATCH 14/81] Fix `Worker` handling of `JobAborted` requests `Job.abort()` sends a `JobAborted` request to the worker. `Worker.onMessageFromParent()` is able to handle such requests, but `Worker.parseRequest()` rejects them. Fixed `Worker.parseRequest()` so that such requests are no longer rejected. Also fixed `JobAbortedMessage.deserializePayload()` to return the correct type. --- ironfish/src/workerPool/tasks/jobAbort.ts | 4 ++-- ironfish/src/workerPool/worker.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ironfish/src/workerPool/tasks/jobAbort.ts b/ironfish/src/workerPool/tasks/jobAbort.ts index 055d11f8c7..442ba80746 100644 --- a/ironfish/src/workerPool/tasks/jobAbort.ts +++ b/ironfish/src/workerPool/tasks/jobAbort.ts @@ -12,8 +12,8 @@ export class JobAbortedMessage extends WorkerMessage { return } - static deserializePayload(): JobAbortedError { - return new JobAbortedError() + static deserializePayload(): JobAbortedMessage { + return new JobAbortedMessage() } getSize(): number { diff --git a/ironfish/src/workerPool/worker.ts b/ironfish/src/workerPool/worker.ts index 5cd46f2fa9..6be11c815a 100644 --- a/ironfish/src/workerPool/worker.ts +++ b/ironfish/src/workerPool/worker.ts @@ -223,7 +223,7 @@ export class Worker { case WorkerMessageType.DecryptNotes: return DecryptNotesRequest.deserializePayload(jobId, request) case WorkerMessageType.JobAborted: - throw new Error('JobAbort should not be sent as a request') + return JobAbortedMessage.deserializePayload() case WorkerMessageType.JobError: throw new Error('JobError should not be sent as a request') case WorkerMessageType.Sleep: From 518c4274512c15d9bf3a70e94ec922463a882d94 Mon Sep 17 00:00:00 2001 From: Andrea Date: Mon, 17 Jun 2024 15:10:51 -0700 Subject: [PATCH 15/81] Maximize decryption parallelism during wallet scans Perform decrypt operations in the background, fully using the worker threads, while blocks are fetched from the chain db in parallel. --- ironfish/src/fileStores/config.ts | 10 + ironfish/src/utils/asyncQueue.test.ts | 135 + ironfish/src/utils/asyncQueue.ts | 151 + .../__fixtures__/wallet.test.ts.fixture | 3126 +++++------------ .../noteDecryptor.test.ts.fixture | 372 ++ .../walletScanner.test.ts.fixture | 1764 ++++++++++ .../src/wallet/scanner/noteDecryptor.test.ts | 147 + ironfish/src/wallet/scanner/noteDecryptor.ts | 221 ++ .../src/wallet/scanner/walletScanner.test.ts | 472 +++ ironfish/src/wallet/scanner/walletScanner.ts | 194 +- ironfish/src/wallet/wallet.test.ts | 711 +--- ironfish/src/wallet/wallet.ts | 2 +- ironfish/src/workerPool/pool.ts | 2 +- 13 files changed, 4482 insertions(+), 2825 deletions(-) create mode 100644 ironfish/src/utils/asyncQueue.test.ts create mode 100644 ironfish/src/utils/asyncQueue.ts create mode 100644 ironfish/src/wallet/scanner/__fixtures__/noteDecryptor.test.ts.fixture create mode 100644 ironfish/src/wallet/scanner/__fixtures__/walletScanner.test.ts.fixture create mode 100644 ironfish/src/wallet/scanner/noteDecryptor.test.ts create mode 100644 ironfish/src/wallet/scanner/noteDecryptor.ts create mode 100644 ironfish/src/wallet/scanner/walletScanner.test.ts diff --git a/ironfish/src/fileStores/config.ts b/ironfish/src/fileStores/config.ts index 85bfb9a246..7110bd0ebd 100644 --- a/ironfish/src/fileStores/config.ts +++ b/ironfish/src/fileStores/config.ts @@ -304,6 +304,14 @@ export type ConfigOptions = { */ walletSyncingMaxQueueSize: number + /** + * The max number of blocks that may be processed in parallel when syncing. + * By default, this number is automatically calculated from the number of + * workers available (see `nodeWorkers` and `nodeWorkersMax`). You may set + * this number to limit the memory usage during syncing. + */ + walletSyncingMaxConcurrency: number + /** * Whether or not to build the full fish hash context at node startup. Setting this * to `true` will slightly increase node performance but use ~4.5GB more RAM. The majority of @@ -390,6 +398,7 @@ export const ConfigOptionsSchema: yup.ObjectSchema> = yup incomingWebSocketWhitelist: yup.array(yup.string().trim().defined()), walletGossipTransactionsMaxQueueSize: yup.number(), walletSyncingMaxQueueSize: yup.number(), + walletSyncingMaxConcurrency: yup.number().integer(), fishHashFullContext: yup.boolean(), }) .defined() @@ -499,6 +508,7 @@ export class Config< incomingWebSocketWhitelist: [], walletGossipTransactionsMaxQueueSize: 1000, walletSyncingMaxQueueSize: 100, + walletSyncingMaxConcurrency: -1, fishHashFullContext: false, } } diff --git a/ironfish/src/utils/asyncQueue.test.ts b/ironfish/src/utils/asyncQueue.test.ts new file mode 100644 index 0000000000..52e9239597 --- /dev/null +++ b/ironfish/src/utils/asyncQueue.test.ts @@ -0,0 +1,135 @@ +/* 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 { AsyncQueue } from './asyncQueue' + +describe('AsyncQueue', () => { + it('yields items in the same order as they were added', async () => { + const queue = new AsyncQueue(32) + + await queue.push('a') + await queue.push('b') + await queue.push('c') + + await expect(queue.pop()).resolves.toBe('a') + await expect(queue.pop()).resolves.toBe('b') + await expect(queue.pop()).resolves.toBe('c') + }) + + it('reports correct length after push and pop', async () => { + const queue = new AsyncQueue(32) + expect(queue.size).toBe(0) + + for (let size = 1; size <= 20; size++) { + await queue.push('a') + expect(queue.size).toBe(size) + } + + for (let size = 19; size >= 0; size--) { + await queue.pop() + expect(queue.size).toBe(size) + } + }) + + it('randomized test', async () => { + const sequence = [] + for (let i = 0; i < 10000; i++) { + sequence.push(i) + } + + const queue = new AsyncQueue(128) + const pushedItems = new Array() + const poppedItems = new Array() + + let i = 0 + while (poppedItems.length < sequence.length) { + const action: 'push' | 'pop' = + // if the queue is empty, the only possible action is 'push' + pushedItems.length === poppedItems.length + ? 'push' + : // if the queue is full, the only action is 'pop' + pushedItems.length - poppedItems.length >= 128 + ? 'pop' + : // in all other cases, flip a coin + Math.random() > 0.5 + ? 'push' + : 'pop' + if (action === 'push') { + const item = sequence[i++] + await queue.push(item) + pushedItems.push(item) + } else if (action === 'pop') { + const item = await queue.pop() + poppedItems.push(item) + } + } + + expect(pushedItems).toEqual(sequence) + expect(poppedItems).toEqual(sequence) + }) + + describe('push', () => { + it('blocks when the queue is full', async () => { + const queue = new AsyncQueue(2) + await queue.push('a') + await queue.push('b') + + // Queue is full; pushing a new element now should cause the returned + // promise not to be resolved + const pushPromise = queue.push('c').then(() => 'pushPromise') + const otherPromise = new Promise((resolve) => setTimeout(resolve, 100)).then( + () => 'otherPromise', + ) + + const resolved = await Promise.race([pushPromise, otherPromise]) + expect(resolved).toBe('otherPromise') + + // After popping an element, the promise returned by push should resolve + await queue.pop() + await pushPromise + }) + }) + + describe('pop', () => { + it('blocks when the queue is empty', async () => { + const queue = new AsyncQueue(2) + + // Queue is empty; popping an element now should cause the returned + // promise not to be resolved + const popPromise = queue.pop().then(() => 'popPromise') + const otherPromise = new Promise((resolve) => setTimeout(resolve, 100)).then( + () => 'otherPromise', + ) + + const resolved = await Promise.race([popPromise, otherPromise]) + expect(resolved).toBe('otherPromise') + + // After pushing a new element, the promise returned by pop should + // resolve + await queue.push('a') + await popPromise + }) + }) + + describe('iterator', () => { + it('does not yield any item when empty', () => { + const queue = new AsyncQueue(5) + + expect(Array.from(queue)).toEqual([]) + }) + + it('yields items without consuming them', async () => { + const queue = new AsyncQueue(5) + + await queue.push('a') + await queue.push('b') + await queue.push('c') + + expect(Array.from(queue)).toEqual(['a', 'b', 'c']) + + await expect(queue.pop()).resolves.toBe('a') + await expect(queue.pop()).resolves.toBe('b') + await expect(queue.pop()).resolves.toBe('c') + }) + }) +}) diff --git a/ironfish/src/utils/asyncQueue.ts b/ironfish/src/utils/asyncQueue.ts new file mode 100644 index 0000000000..45dec47232 --- /dev/null +++ b/ironfish/src/utils/asyncQueue.ts @@ -0,0 +1,151 @@ +/* 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/. */ + +const EMPTY = Symbol('EMPTY') + +function assertValidMaxSize(maxSize: number) { + if (!Number.isInteger(maxSize)) { + throw new Error('maxSize must be an integer') + } + if (maxSize <= 0) { + throw new Error('maxSize must be greater than 0') + } +} + +function assertNotEmpty(x: T | typeof EMPTY): asserts x is T { + if (x === EMPTY) { + throw new Error('Expected item not to be empty') + } +} + +/** + * A fixed-size, async queue, implemented on top of a circular buffer. + */ +export class AsyncQueue { + /** + * The circular buffer that backs the queue. + */ + private readonly items: Array + /** + * The position of the first element in `items`. Calling `pop()` returns `items[start]` + * (if non-empty). + */ + private start: number = 0 + /** + * The number of elements currently sitting in the queue. This can never exceed + * `items.length`. + */ + private len: number = 0 + + private onReadyToPush: Promise + private onReadyToPop: Promise + + private triggerReadyToPush: (() => void) | null = null + private triggerReadyToPop: (() => void) | null = null + + constructor(maxSize: number) { + assertValidMaxSize(maxSize) + + this.items = Array(maxSize).fill(EMPTY) as Array + + this.onReadyToPush = Promise.resolve() + this.onReadyToPop = new Promise((resolve) => { + this.triggerReadyToPop = resolve + }) + } + + get size(): number { + return this.len + } + + get maxSize(): number { + return this.items.length + } + + isEmpty(): boolean { + return this.len === 0 + } + + isFull(): boolean { + return this.len === this.items.length + } + + /** + * Adds a new element to the end of the queue. If the queue is full, waits until at + * least one element is popped. + */ + async push(item: Item): Promise { + while (this.isFull()) { + await this.onReadyToPush + } + + // TODO: should we consider allowing only powers of 2 as maxSize, so that we can use a + // bit shift instead of a modulo operation? + const index = (this.start + this.len) % this.items.length + this.items[index] = item + this.len += 1 + + if (this.triggerReadyToPop && !this.isEmpty()) { + this.triggerReadyToPop() + this.triggerReadyToPop = null + } + if (this.isFull()) { + this.onReadyToPush = new Promise((resolve) => { + this.triggerReadyToPush = resolve + }) + } + } + + /** + * Removes one element from the start of the queue. If the queue is empty, waits until + * at least one element is pushed. + */ + async pop(): Promise { + while (this.isEmpty()) { + await this.onReadyToPop + } + + const item = this.items[this.start] + this.items[this.start] = EMPTY + this.start = (this.start + 1) % this.items.length + this.len -= 1 + + if (this.triggerReadyToPush && !this.isFull()) { + this.triggerReadyToPush() + this.triggerReadyToPush = null + } + if (this.isEmpty()) { + this.onReadyToPop = new Promise((resolve) => { + this.triggerReadyToPop = resolve + }) + } + + assertNotEmpty(item) + return item + } + + *[Symbol.iterator](): Generator { + for (let i = 0; i < this.len; i++) { + const j = (this.start + i) % this.items.length + const item = this.items[j] + if (item === EMPTY) { + break + } + yield item + } + } + + clear() { + this.len = 0 + this.items.fill(EMPTY) + + if (this.triggerReadyToPush) { + this.triggerReadyToPush() + this.triggerReadyToPush = null + } + this.onReadyToPop = new Promise((resolve) => { + this.triggerReadyToPop = resolve + }) + } +} diff --git a/ironfish/src/wallet/__fixtures__/wallet.test.ts.fixture b/ironfish/src/wallet/__fixtures__/wallet.test.ts.fixture index 0536fe4d89..53339cb8d4 100644 --- a/ironfish/src/wallet/__fixtures__/wallet.test.ts.fixture +++ b/ironfish/src/wallet/__fixtures__/wallet.test.ts.fixture @@ -4329,45 +4329,17 @@ "data": "base64:AQEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/5M1dwAAAAAAAAAAy3pc2+JAD/A6NPIf91xdfQOKHCPwRJUvnVM/HlHbMHGXLfvuZ6UWoYCmQLyttYvDz1YqcfEHFmDQ2Ag9R0xbUNcPHtTmSbnfFN/MfB3aWrexE/5tgi73cO5zwZbC3z2TiObav6rtvfnpo20tO9YyiSHuvtscWXu/QS1Vx9HxvDcPQldpH4/MG72Qbgnon9H3eYhlwERwmncUu7kJNuqEghy54cs+YEN3KuwMJOhlFcyInhQbGKbXwODN2nfYeYtoks/hE/PXrI30SScVifsGbhkF2zvdlhAp1xvRhXF8a5lpjF/HPIsIwAbXG1uk9OJsIkCVqCOCgWtchLEd3lVo03jonQzOjkdfLNraJxUVavJ/Z6jrz6wUDE3Lsfq5tbVTBAAAACSLreEcoFC5KqBtvB5xKHIti9ukKYEWL2XCBFIKZlMNspPwzvSk2F/86DM5iEy95xtjj7lbvTESBobNj4nK2yl/RwpwrmdRG46vDtDhZSmvHL7tYtuHfPdlYQBjX1XBA4cxv+RKuQAi8CIr6F1tA0bBu014pHPMEO8z7FcR2+GkvrYdHu+0/osVqUFP1ADbvoFSQGjz+yWC5UbdqBtLMSDs5BJ2DMJRw1bH6OO/PrA/Zjpmp7/goKG20XRI1eMBNwDbXnR+dFMCYKJDaglonp1wTTY1XMMBTtZUpTf8W7qNirvCSpk42Ye+dBVvlzzREKDxZGn/+ZQeArr1NMaEKZhdk8Xt5YF1Iq+AouEEG8iABRtWXUS9A4kthpRSysAxey8jQdNEYUCeqBe86jutnfx/O0aSLhNcBtKD9u+nFOuGeHvcGMs54SB79DWe+MW/RxjE3dvxS1qDINNf4Y6LbDfpSS31Iko2I7xXVR6KxNI59Ki5GDDlXO/YmNwKWceMNyMMlI5SyNy5fyuBzv9sxEXfuouDNsztUvbgGf4z3Ny7IcILbGG2/3U8zXS/lqUn5rbU9ev/chBkC2gS6pZqQLl2pdpuw5lVUkEnT/+osEvNZxNkAtah8Zhw0hwv22tqTv2jtQzp9fePG5gKQZGW+RJGKauYPaHdjH3IFen7WDNq/2aXUOHdMxeMMKFVyCMl4FtDl7fP0co9PxtyUH358Z9bXt0K+ENmOvH0MgBkIXvZz/f+EYzSTqnrIYT+wJ1Kp1BtL206RByfqHE0ts63KH6WjH6+B8B0Jf0cHfvMOmKyvvyM7IbHlFNKCHOchA5kd3Zmz19NujaFoWY+87LlvDykuCnkP7TcH7slZlymjBm2vvO6CxP6BOuazMARTZoNvzQ/jHWJigcD" } ], - "Wallet connectBlock should add transactions to the walletDb with blockHash and sequence set": [ - { - "value": { - "version": 4, - "id": "98f0d037-134b-496d-a85e-f90a7500162e", - "name": "a", - "spendingKey": "5976662788b5abe5463fea3d9b0d49bc66cb02787a76c8f1f48da6034c6b0755", - "viewKey": "a773dfc42b64c93c64f49db6923ea489c8c59c33b0cca1ccd991037363e5da54d4617e933c0468cdd701c88989701372fe97a41e8697bf561eec28475bf5f60a", - "incomingViewKey": "078133d8b9f9055417dd4b03653ee4b8f251e0fefc32149aa7425c9d3fe6ae06", - "outgoingViewKey": "815fe80a2f67cf8e8228acfe9c3d6bacce5dd4c9f5afb3162f9fc73d58e1f526", - "publicAddress": "92f7010e34896fd468dff5af5afcf15b2ec2c126cf324bd7d7f58c02994c0a0b", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "4f062833d1893d42e79808851b14d82392a2053853f6747334ea4db38a5ec60d" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, + "Wallet getAssetStatus should return the correct status for assets": [ { "value": { "version": 4, - "id": "d1ed30d5-7bd1-42b3-a2cd-d8d0646f31c9", - "name": "b", - "spendingKey": "60c168e2d9582bfd1a68e86cfcea846b6c7fb5be8fe85450cf3900a39532aa49", - "viewKey": "edcc327edbde105cd7b3c5ec014a9018ed879418c0220b0da681bb0fa78bc1010a6ed96bfe3523853608a906d2fd2ab4b4fd3bed9376a00e01c00787e7f4a7af", - "incomingViewKey": "f9a76c3c240dbe1053a9cabe640386259fc6f8a0a7e86bfddfcf14df8fb72a04", - "outgoingViewKey": "1000c09122ba07b31e1a053808e1ee52ab4fcf82b1f58677b14bbf6664d90612", - "publicAddress": "7f6229c26f5ae7ae1d2460f66a49e21a0043738f63ab9919e29ce8af0ee227ce", + "id": "9ce72f1e-3ece-49cb-b65d-74f015d8868f", + "name": "test", + "spendingKey": "7d1b498c8d3cc4f73e73e6fd478a9365b0d8531ff6164c02ea4a082fc4f62153", + "viewKey": "ec221089ff616478f18ac4407a18ddcb1df20258cd9a573c50e16a16b5e1e4ec78d86afb3a27ad9cc1e928c20f0ad5dd983a1ab1043962a4679cc95f0821102b", + "incomingViewKey": "a133eac24bf5a25cc97053c90946ce9463c4504cc0cd1ec5197c2e0ffab3da01", + "outgoingViewKey": "85720d0846bd0c593b496c79637b57e4b6a0f2ec525d157424918d23724d4bfa", + "publicAddress": "bfcc2c07f689708132f5bc4e04d9457770885b1835152dc8d5a78a11ebe32eae", "createdAt": { "hash": { "type": "Buffer", @@ -4376,7 +4348,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "815ee445b678ca104c39396e10d94589ab3244a3852003099fe553f88fa79c01" + "proofAuthorizingKey": "9da0cf11902fe7853a0efd7737c8d409f3f06ad031eceb3f0bf2d1759431b904" }, "head": { "hash": { @@ -4392,15 +4364,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:tKOY0dLABR77M6xoQzhdVHhV0mHHw19i2WaSx+XiAxU=" + "data": "base64:ljyAKZkpGtZzzVxsP+MLmB+LBKtd2d46th2i6C1y6yY=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:QrmgOXZtL3Oi3eI7bEPgD9dCzI20mna6NxPw6f7Dcf4=" + "data": "base64:nCDFbONWvWb7DMJ9Kn+enFcxmO9FXei5rvmcHC+1lzw=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538675075, + "timestamp": 1717538810002, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -4408,78 +4380,56 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAWS90h4QTrgg8wrRxgd/n+DwC4c2h1SrvjjTyV8W9PpmAVzt5CoHiZPXsHY/H/jJS6+xm+oP4KSshoiqJUQC/KGZACTa7PlEPWeSB97LIzQyuNFxWCVP+7wXwUA0fmizNXKj/slNYz+pFcg9RncilXi1htyTanKlHBPuoqKwyKBQF87i+yN9YBwLK8may0ETBN19Rsghcx32qQwEvhIvJWz/z+9OKX1Fw8It6DdNtPDyuHFSQthQHFKBm49dfVmW1jhrfyHPknsz4xHrCgoqcUgS76yxm58bJ2DKeJz7WysBOxBcEqYomsGuVvwpmove93CkdWd6BYzEzS+SjkByCyzdU2sZr2gwfAn+Qxq7PefzpzCzkC5i6+hyEymlfy78jIlO4cNrA3XkKkgrJCa6JAmrxjWmigT/fRJglVWDiJjyaqDxe8w/pOohs0UebXPQwkqo6gwJjzXbpsx8Ol+9V1c0KqigMKY84ybMM0/ovUygzgYFivqXg96NxO7uvqACg5cs6vwpiQnxZ1DvFMODqdPJ4p5uxqxZPvH7wGnglto2CeBj3eNl3NV5iaVSW88Sh8hnCwrprHlQesAesS1H1IkuHQR7pffrGiNwh9GSuzGc1xmOAf0umq0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwaUm+Qeppu0oF8bLu2SPyFpZEZ8aI80YIUGjJMXyYKbpEm5d84nUItiITHfCLsauZHRUYJrvp7kPE7/5xD/x7DA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAIO6f0wCjg44SxkFkq204mtRvRYfkZb9oiVMSqrTUa0KhJFzHrNkZmy6ZttvdzFoXhEgdjLVRTc8x2ye7DRcF9JtLSl8KlA1XxayVNFtFLsaM1u3yZVcSN1YtLI0+woB0Zdvkj6CIPVXruZOiMYn/egap0Z0br5EcRbCTVGTeST8BO+8r1mzD8DzMqoAQbYG4xHwg5NcO/a3svvTNELC4pj9KF55LHOhTUccRfsiVPZ2P1OLx4A40LjNN8B36r9XzkPDeqs2QpWDMmCDFvKRmwA8pR77wylHzBbCNDxhwdyYlRVmmGR2yOP5Z2sO8/phrXIgasxp6z5ebCAFA4OEVubwnQ28zb3D/kWSPgSIvNvo04NoguTK5np9cySOTDTBNv06RwKYNE9jq1834s9i9pUZgEv0HmyfZnsZYJm9VyTk2qrsWE+wRTMT0s0ckWpqFJ/RGhj5EufQ08Od/6YHXILVD7XUpX9SG3Pi4p/CgDWX+22wIXR20pcUunVZCcd5C0rH4q613VtFU1L3o5qs7sQLNmH+x/15mnrdpAmpB/j4OhyH/VpQsT4skZ7BRJEQXPAAvHaB/zphGSD93D20Wf92yRKd95QYWtaf1Z/y+R6AH0cUHmXm8KElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwfB0rqqpXN4dcLtAKd1ebu62PbRC8xb3GlGApQjCmqQalIKDnOOrbq+bktmrbJUYbpdiwnU2VI+h0SrlS72OMBg==" } ] }, { - "header": { - "sequence": 3, - "previousBlockHash": "8C7D69EC69350E99BA5134807E12DF7D509035CDFD43503A05FC9957663218FB", - "noteCommitment": { - "type": "Buffer", - "data": "base64:MfFAgIf3mNhgU5WuhibYCyeKdt6aYTW21hIDNub5/TA=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:e7Qh3UOumiT6B03yJGggqNQdEybG1VbQk6/IXU+g0kY=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538676699, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAVo+0WQyMVWVrfe98CdaUMQbSU58ekKeVpOwWOApWdIiJftxH3+Uz4XWhZe++7z0kyJDB+qVzUyprfDYd5hgq4BVbmgzUu5yhxS9x6MM3U6i5f3OiWKK0Xh/TfqK0KAhi/gkcnlafw2Oxs95MQzi/gwAjP7AwjdlHAwUmHR+DrDkGl7sRNcmH0U1aSKjIU/CCB10HAUfwDikMsPalDBfpx8RL439M49dLi5rCuXavzQqI2vrH+TwbXSUxoEhEw5BX/b4f1FoBAInvu31/lVPkD7ioyZgplC8k/UAPEA8acw0ErHekvWuUqyxVcOEp8t1NPNR5z+V5ux84xSe2hv8mvbW9mnIdgf97eeoJu8Ho2Xyr1udQvG0DYwEeJXOww6s6NCWMyqT6HIv8vyE68tADSKX9vKoIHr0rSeY7b9yfTsDTSBuhDA6Gw/jKmxsdNqjHX1q7wFxWYlRPeQs0QAzV4G55Fu9dSsX2ZYvr5cdZF+CaF1NVJIiT6rM45KPwwDurmUTedy8QxkzQ+0PABc6dqtMRvwzI1VPvPJInHijJVvb5oR2F2yb9K0jKvS6mupUT2DnmvlEjKJGMqOrSZ1ESxSzFdS0ZobMa+yCUrb14lZWknVDjiXTTxklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwEL/U1HhIULe4WugQkf+rt3766vBmuBndiy+Nr9Kn08benLWssKAVpF2pln8P7kikD1jfWfqyoK6TfeZGN/fEAw==" - } - ] + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAx1XG8nmZYHQ1lZDF28yrjNdwPtLmJXorkmHX9wpd2smEGUFH1c+R1x90Cx1/C++ZDaG0LZOzFoURhJxAgkQWtLaKuHgy4X/THyeEIRF2de+rikJf3pai7Sl1Rk5keZmyEGdca9g1R4j11EvorfIueXMu5tUkSxjXGQih7xj00BAEzGD//L2AFiegE3c7DNOXrcmry9GugoJLx0NtqS+rSFuQSJWJkifCzus9NzVT5DWDpPIC1IiQe5v6JhhNCAD3bawctCBU4KkpKbzeJTChU9EJ8Aexj6eRDpAlecHOsVGGCsJ0dz1bBRkJiQ9UnGQ2IaU1CRRecDT3y2tEEZh3DniqfyVpGJdU6HL5I793Vf4K9pDxdBYAzyBR5hkQ2qMrPC7D7dcJHAmBEEGkBPlB25ztk1sUmFJNHlIq/E3LJm3E61ASn2x8lz7VW0X+J1CrzBetBuTa8P1WyM34ygGinGtIbuixAvKTuBPD3D1WYHUL8AmCSLBd2MQVXQ4QXhkytGrV71vL0Y9TIQUUmsynj1zfwRDdhGwl0JEAQKEaydEm/MGpIgKMunwX17UIT1DcRHZs8QDL7V5ddIHkl+pamlvYOqTG/Uv2yY4gt7+/5P9cKsBew8hqBMnYroNLOiCPvcwZKFcQl7wbyc1xBSswaeLuNGt5TtT27T+9Dr/HzejTvH8KfCGfBD68uJnPQsK86SBZQilhh6ix2IGJIsMAh06kCZq+XQoBoFkdkqFZd+4RcgcjGpbRMA+YBC7PguW7foeHd0jEiouRNB9IcfndU8o3VUtdabdKkYsF5F0XvJ+V1+586fBd1gJ8P7HZaCfXlVbvyTh8vjC17V8G0LSxSdvnpKr7iOgvD4l2mFXwK2CnEwLRVNAVR0j2WiUUknP0PDm6N9u2sf3xctF4ldJ/EsaKRN6kDSpgooqKnc/d6JHoWKN5KJj/MI5L6FnZzO9BS9sesUewkJiNSpAYd3hvVuz4C2buMGj0v8wsB/aJcIEy9bxOBNlFd3CIWxg1FS3I1aeKEevjLq5hc3NldAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEKAAAAAAAAAHegEyym/Ucg/MHeuws1hyy+jM+zhif831l7UTldqpaTNPaIs2r0vnhH5D+wIM5HCifg0OlnTjV7G2mcuziyfgc9gwv2Dr2csHNTeZsubKTYJwSSmUTRfzwujsxrJaaUv1RZoQ1ta5XLLCMBXq33YxSofd7+rr6rAi8TArKGCNUK" }, { "header": { - "sequence": 4, - "previousBlockHash": "6848F80E8EF39DA1362D4CE54E888666B6C785AA6F390293BEC242B8D916C2CB", + "sequence": 3, + "previousBlockHash": "2000D5E8FFDBCD7F4A0B1ACCAC17F750C344FE3B9B927E088269693C14B28C0A", "noteCommitment": { "type": "Buffer", - "data": "base64:Rvq8x9E7Bj3wp8V9I+x+GMw7t90akR/qhUkCa4u1LyY=" + "data": "base64:8S62RRIAfpSANrCm3IxZu3YlcBtQJrQcAQJu1v5QdRs=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:EyITUATYp4ngn6mMIil6iSCEuvhBdVmZn1HplouA/qU=" + "data": "base64:+NGczX+1gjYFWQjmPo959KmEs7QAsV8p9sdVuJc1bOk=" }, - "target": "9233318228143625020618577701423519925017621426082203201059080050516648", + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717538685227, + "timestamp": 1717538813921, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 8, + "noteSize": 6, "work": "0" }, "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAA8FKDUmlqZcbfq+aA4V4hXtwi/Mw7Jnwj54EuUFcXk4SxpCGQbSE49dntwcqkRHKjwAl2rXEh2EQ3C6PbDZJU3tIliBwJ0ErNm68C5BHux6Wy5FM6BI9pMiYvbFFZx/t9AmwKOc6hLvX1aTHbntRWW4BKfPWTV5QcW8O9mZXD+tQGtMLq8oHNBHQDx2VlUzPR2OPA2ScQ8r6hpEpgs79i54faiPoSGgmRcExNUBJQxeOJoJzRPREEBYS1FA5VZ+fYs41cW6m/9qcxEkavxd5RwYHMh9pj4rOXGzqVjbJGJtgLRTOjv/yQpIMFRVKsv5NmFwVBEbJuXJ+wK0nVxLYAnTNQtK4nosXm775hZhy4FOYcRWcTswpFE1oIHnUaGeRCV2cYWfH3qoO17INMMQDuTzmo7J0mK1HqRPtV6ksWX8iY5ylbeoxjf+lNE6n3BTE1q2Q0VJwGprm1J3+N0qXOOWvP6S38bSvtsLGqQYbs4SeJDzuwWOshAXVfZBjzMTQkN+Z7Q24a/gameEWs7LdYi9FfGrTUSauXCoPO4eAqmEybC7XFrRBSlUMqN14KbBTZwx+hYYNyIsP/QG0JtxonVO/6J0cONGRgiAAqEGhGrODlXz09t1Mm2Elyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwGuZvcRvviO4INw0bFf9Zd8V7bLANBBmjSih/otrU8WnuId9HT3kcGA/jmhOqMnSqwTi6YoV/Pq7020hleE8+Bg==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAyRShcULppVi9LURNIeUTgqVKs5/2NBIGXDchmWTbDOGN3WiWZ3tk4yaP9BckHcup4FbSxtyVPNJa0jiHdFppxUAxRBk687ttGhAWVqkY3ZCZRbXybGPSW9EfrRL35y227I4qnga9M9DK/9m8mFkJ78f3B90P+ILAB/HZwfysPJEJFIEiX1O5pAAtcSE41CQj4NUzKyn0ReNwqSC/+sp2jXjdCMmcrFlNR68q+ppFbQiLS/7EEet+3SZO6OYLF1N5xzucRYwtFSWtCIQb0HlKSj8ObuJMX3B4oaiXlKaEXX3VRnyRdQRJwcWiGaPrbZJLjzUVt3fEE0Jq6AnJNjAjrp6/PW27kDsgDAyZD1Qh+xEFNhPf7pUmTb1Kgk1dD2YTfFx6dwb5NwGaBTIsQIKano473Y9q1fFWMLBBKSD4JtfuPRo+aN77/LyMuBkScG/Rs9eT//pEhf/xnPW1D+G45XG4CF7q6TjhZOkWJ7/IlXnnIskhl3f2eS47zvfRNZHrGW6ZVEuXDF+0hlbopNX/SFhZIU7n7UyViwQYnU/t9WLaDm2G8P4kDn/7ugpH6o9geblFd3P+YEkx1mZd44c99XsXpjyDL85QiOia0B0CylYQc4T5kNM3wUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwSFe9YgIoJjUXjpiKrnrwtdncEAuFWPoaUez72Nto6unZYHBkzsMqSP4/g4Coe29M2MlNEBASUzWnYD18meGKBA==" }, { "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAA/Zecsdz5QXXjPvoG4n9bM6ifxwdMbWAR4vXIX3nayNmWtr0nOoEfDZnZ5ZZ1chEWv4HUJ7H+oMYTIkm0Lthr4FrAfo6mloz+MPWKr0zzSqC3ySFPzF0hNsdp6tb2oo5YVjRAmuTgIBrBzsthqlkYM/7qcEecOOYhLvSzvHzNQhMAqClHVAoj39QbVHfZBWHyTUnanUxVFFMgeO20436j+RWBsvj7sE+3FEJ48q+gm/etUpa5zXG+KZ9VHtjD/GLzdb5k/Ojl3zX5mNj2NGVAuTGOOLPv1X2fplrypruaAyQ5jXKX+qbJEXlzC2fOnLVHg6hmNgJ2QRxMjcyswbr/TzHxQICH95jYYFOVroYm2AsninbemmE1ttYSAzbm+f0wBQAAAPFNUhwZrRkUfPtW6HLktSpJ7Le1oyzo3SCAWzXqITrwfNeEZ7i4oGvrhtWntpDOvGAeeNFJm8G/eygqF1lF9OCscbSzj/4xt8DzLarIIU6Ce37Nvja8rM0LWdFEZoeZAo4Ens+z4DW7sWjftqSzRYtvUTR4wl65r9UlsXZ46XxLis7ZXtTkMVHaJa8gHAs0U64vg/lWDS+kBTdw/M6SlWOvVJDBqIvknThEYD2BFGOSBjcaxqmoIAE7trJNaqibvwq+GwLNf2kq26LipGgj1BuTnyjVQESQlmiRKRXCkqm2pAjggmjScSXIKQcMrLmO4o3d8+/UkcYpGU2uFTOKOMwC0ukIjkAGEQhRK1fTbZJZWso1o4SIhIce88uZ0JzACtGhEZWKUmu01WrW/vTC8UJzxyRwW3lbAK1dZXC4YMLYKSHvPbC/lHrdnK/I/ZOSfO3VQAUBtJ0CZpsgbScXiRN0IngYLa4HoiWQ/efOmIK3p8gj1ZsYQlVEFR9xiQGG6UrODTaZh3gRHftJokTiObM1l8il2dkep5JgCk3R77h/hGPVi7FydkGGsMq7zfgVm5lu0PGPJEuY/Q7SkO8hPcox39lmpOGRK4dTLxosk1HqpRBybj8eIJtgpkggQZvZjMM0n1jRB60ssV3wVwoPkQoYTe/IAXG9q7q+moRFMhyRsxRWYtPobyNJKaTTWSjXUEA2jnr5i6rlyCMHsPehchTvtjyzLik1U8kW0PikxrNltXYbwVZ2VHNgNBkbVX+i1x0fX2j0wvlvWrg8WX2ZmxywPze7MQ2g1RZflj940d9onEtwJaDLCteTkK4PtS+s5HY1xTcbmNSmLvATmI/ZLa2F4zR7HLZbMPMAjruPkWaRiOZCF4/JDjmyJQFB21sY0FcVuSZfaTeC8jFlh9H201pheuLz8/4dwgpFT7xthQ70fhTB0QgHP7cDDJ2GJcrjCbQI2vvNdpL/d4DP9ToZIucSRDhRFHEmtSpFjUuRzExuRoGbmUZ6sce57omN2DQAyzHuAOL9gRsVGXvC/9BxIvyeVwe1al0z8c3/JM/jAP3yAv4c1PqfUYhRG5h5GwdmMD+I3AEzJs8t4K/sea4VzazqcLJrzqq+OfmE7QCb+XrVQi9eaSq0DPCytU5hqlzMbJxFdiVZrfka2VwnkvPFBpTYOfXwOEClLqs1qVReV+LfQK0KKwVTitfC+INA7jM7u05crvS6VYfSznIXDGZS/UjqhbBGuaVU5Yq6FQjQnz4g5hYg1XlhonFOHehZ99mPq/3WthP7Zp0gJHfVJztKqXdailAayhQuLPjtFzjVIklAA9mRGSjXGU0QG0KtNtqAHsCC254gNhLKnZXewHV8whs77Y8onC7TLWaqTKTogaIwqnIPqfCFk6WXnY1n5PGRcerzF+aeygElp4u/00Ad71i58iDPDsG1mfsJDtj3q9SyrlkqOxuPG4UEIc1Mi+FEALwSc7EPTNPHHioLLR0cCOJuqHiL2pirJ8/EXDbM9fz5SMfo7C6BUWla37QCa4l+VKzLlBJUDLKpqUQBuGMAOG2UOXt5K3K7rebyQwkZQJvtwj6AxCb8igGyPXgdr6sKCQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAx1XG8nmZYHQ1lZDF28yrjNdwPtLmJXorkmHX9wpd2smEGUFH1c+R1x90Cx1/C++ZDaG0LZOzFoURhJxAgkQWtLaKuHgy4X/THyeEIRF2de+rikJf3pai7Sl1Rk5keZmyEGdca9g1R4j11EvorfIueXMu5tUkSxjXGQih7xj00BAEzGD//L2AFiegE3c7DNOXrcmry9GugoJLx0NtqS+rSFuQSJWJkifCzus9NzVT5DWDpPIC1IiQe5v6JhhNCAD3bawctCBU4KkpKbzeJTChU9EJ8Aexj6eRDpAlecHOsVGGCsJ0dz1bBRkJiQ9UnGQ2IaU1CRRecDT3y2tEEZh3DniqfyVpGJdU6HL5I793Vf4K9pDxdBYAzyBR5hkQ2qMrPC7D7dcJHAmBEEGkBPlB25ztk1sUmFJNHlIq/E3LJm3E61ASn2x8lz7VW0X+J1CrzBetBuTa8P1WyM34ygGinGtIbuixAvKTuBPD3D1WYHUL8AmCSLBd2MQVXQ4QXhkytGrV71vL0Y9TIQUUmsynj1zfwRDdhGwl0JEAQKEaydEm/MGpIgKMunwX17UIT1DcRHZs8QDL7V5ddIHkl+pamlvYOqTG/Uv2yY4gt7+/5P9cKsBew8hqBMnYroNLOiCPvcwZKFcQl7wbyc1xBSswaeLuNGt5TtT27T+9Dr/HzejTvH8KfCGfBD68uJnPQsK86SBZQilhh6ix2IGJIsMAh06kCZq+XQoBoFkdkqFZd+4RcgcjGpbRMA+YBC7PguW7foeHd0jEiouRNB9IcfndU8o3VUtdabdKkYsF5F0XvJ+V1+586fBd1gJ8P7HZaCfXlVbvyTh8vjC17V8G0LSxSdvnpKr7iOgvD4l2mFXwK2CnEwLRVNAVR0j2WiUUknP0PDm6N9u2sf3xctF4ldJ/EsaKRN6kDSpgooqKnc/d6JHoWKN5KJj/MI5L6FnZzO9BS9sesUewkJiNSpAYd3hvVuz4C2buMGj0v8wsB/aJcIEy9bxOBNlFd3CIWxg1FS3I1aeKEevjLq5hc3NldAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEKAAAAAAAAAHegEyym/Ucg/MHeuws1hyy+jM+zhif831l7UTldqpaTNPaIs2r0vnhH5D+wIM5HCifg0OlnTjV7G2mcuziyfgc9gwv2Dr2csHNTeZsubKTYJwSSmUTRfzwujsxrJaaUv1RZoQ1ta5XLLCMBXq33YxSofd7+rr6rAi8TArKGCNUK" } ] } ], - "Wallet connectBlock should update the account head hash": [ + "Wallet resetAccount should create a new account with the same keys but different id": [ { "value": { "version": 4, - "id": "203693aa-71d8-4bde-bd3e-678f96086a70", + "id": "f53847de-227a-481b-a812-d7d3bc470f79", "name": "a", - "spendingKey": "5b1eff525bdc0d699700370b4c464428bdc20b2f8901f07e9dbe025eda98842d", - "viewKey": "523b241521730831004ee05525dc7d80d949398e042370c55efb282ba0a6fc94690a8b2ec4bda70bffb676ac1b3af78558727c749797bb10074866795422233a", - "incomingViewKey": "1d8b5c646e4dc78dd5b114c9d6b9985ac3b157f2b1f3b88d6dc3274285087206", - "outgoingViewKey": "c0c68212bfa326b18a81662901f02550f36cdc6f902d5fda40f059a5012ed429", - "publicAddress": "1d36110d2e6c8dd0f700b747596cf63d62f743d13b8d8924055d96911e9333d8", + "spendingKey": "8b98a67d7682196a381b5d60a2620f2c91e5b73a2b2e3f7fa820df66658799c4", + "viewKey": "20775b11a44869e536657449c5855cd0915b71a7113bf49c743606420414273852c0c2aa8c2750a118e1b8a6a2203df3b344438b7c2ed1485385ebbec84742cc", + "incomingViewKey": "6109c2999c98a2e918b72c61e86da7f0e0abe9149bdcc850bc27a1eb42f44407", + "outgoingViewKey": "1f5613cf9e3adca3828dbc5d240fb945f8737681782080eb49a4f7e5a97a144f", + "publicAddress": "9219c0cded1632973b6f30fe89a1ef5d2ee497a43a9e8b4f327ad5e2c74bdd09", "createdAt": { "hash": { "type": "Buffer", @@ -4488,7 +4438,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "63c9a8367bb057cdce77b16f994e828cdbaa5203c41cc2e904f2b12252639208" + "proofAuthorizingKey": "363be292208a39201f35e92ca187ce0c4cd09dff07b555edc2b1af6d38158705" }, "head": { "hash": { @@ -4497,17 +4447,19 @@ }, "sequence": 1 } - }, + } + ], + "Wallet resetAccount should set the reset account balance to 0": [ { "value": { "version": 4, - "id": "c182e51d-7e0c-4074-9498-d47d48d35f17", - "name": "b", - "spendingKey": "4c456f3b38c1731304e5d4df3d0ec6b0cc06261dc58f7873eff2d598038bbd11", - "viewKey": "3b027acd91b59518793f55287f1b2811abaf5b2b0cf45fd5750d2573d8609cf32c15fab25f110e798d3cdc0a8840b8abfb7e4385bc5f4b70e17499eff43f463a", - "incomingViewKey": "04ae8d2c315fa46c1280ad3d773e5b9af5651516439929fddaa7a652f07fbe07", - "outgoingViewKey": "c239e74af1eeee7d96916801a4941f2cb7fdd9339281ab9d1921f75523cf6268", - "publicAddress": "e04def37fc0cab193e44a31390d85c0090a2b89a0610bc16e017f520aecf926e", + "id": "6a9ff94a-d3f6-4fb9-89f4-0ba9f707c04a", + "name": "a", + "spendingKey": "937a478517b57e3edac6b5f362910c7333d95fcc58c6fa400dac30b05441e57d", + "viewKey": "b443df461a21c2f783a01d9735720aee1df503aedbfdaf8c7166f49b2f2e801b96e721162122300197db9d96e5399154b732711c15d06b76ae231d2fbaa989d8", + "incomingViewKey": "e19764232ec43709b83322ddb76d7a1cba8276194e97bdb6ccf0c457aa19d200", + "outgoingViewKey": "3dbc7a26ca52b85339ae4dfa94e90394a15e162081d537657b565c1a65554a57", + "publicAddress": "b4230d53076043fec8aa43688879cfa6a3286f6a5f3ac64719cf9fa33744d206", "createdAt": { "hash": { "type": "Buffer", @@ -4516,7 +4468,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "502fe91c0be9444219abc053a14247785e8eccf5794d8af5d91dd3ee7ac4bc0a" + "proofAuthorizingKey": "b623eb9b8d6578c459eea70e0e562faa59576cdaba377cb66bc6e49e2ad06b0b" }, "head": { "hash": { @@ -4525,101 +4477,19 @@ }, "sequence": 1 } - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:8ZWbGvok4PJT2Na5ti76UPQJxVICBSKArkfCrCz5VG4=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:ctdXcichGfVii7/ebZ4DB8mOr6WQ6szgnpV0msPeOag=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538689366, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAATQiUeasNPZ0R+nL18MSm1AQeYU6LCBnTQgQUjMbUXoiMjLW6uTSiNmZLMs+2YlY4YUdt2rjyjmnsZYDxfKy4ZDm+fx8oD7oWy0ptSJ0TjBmTE4noa2ggEkQPdcfBYBZST3j5tMQZN7qGQED/6wNGyLr5QnsOe3f2fSx1vjv2VgcG30Vb6ss6jROh2rRliFfdLler/1OBds31Ec/ceJ7dValxNOfLgSur6Gewky9NiayJ3CRZLeGAH9pXg/W3KPhL1vVE8Qgx7oYwsjGYTM2/PNftNPhSaJtd5BEgQT5/v9s1Hc5sl5ezbKRZijzdFE+X645t2Ed4wM7wgcl9jWxKtQk/fUmTELjol8Si79LCEtVDxgpel1SYuIix4ZLV+LwBeNnEn0b/2/V+U13A16JDnxrycaNSXeHwxk2t5BsOVpqAD343MpH2QlnJ6jOCUnycnuUmFggjCK9Mnc7HjAVX7u2UDaSz5iLyuSSeBLGrbjmJldeSUlnJUgGPFCRuDsad6cyCQQcvxrqQkNRGwkmgwpK02umxDDNMqFSOHCCi9T/NZxH86r2pLdECJ1zLikmSEo9sGG+uMhJQ1GiSl4h+cUrVPo21d9CgSZ9ZnHb6mSiGEW/LSrFc2klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwlw4U1DmDT5USNY4ERWMIjyTF5P4+ggRbiAErPcr8gHLtDQTVMGjQi1AYNQHEizcXU3oKfZKAR7+g9RzevUkSAg==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "C0ECF2D4F0A085EA8A0ECC2C4E3787BDF860A49AB80C867F2E780CA41024998B", - "noteCommitment": { - "type": "Buffer", - "data": "base64:nVr9NPL3fyAsgr4Rcz1CMjGYm1134UIZcnCQeibt+0s=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:gZLBGDeTqRWU+lMcr0wmUPa8co0ta46GKmaTmRei1LA=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538691121, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA8mrZ8TJAL5muTNSgi2cTHoeI/kzCXiKKb6bDkzSZmQqDQFgBZQyv0vPQVxJCl3lCzEXxJi4tNQJ9fV3WZM/WYHA5iMF4YguK4f7ZQOSlp4agu3Dj/ngJVx8FcBW+UrdGKe+IR+yJtah3XER3k33zFucVaGXavwB++fSi6jzwXCMQE14xzYW7Y7Wjq7eUw1eKqv2rE5NsXrTkUlsOpEhWDf7anq/W2sH5StYdxqor21m0DivzRapxmD+jMBvn1Js/Vr/j5gtFAVPTPxa0klPqqOZ9o5c0bMg06P+DrgzVzofjNaD/KzevGEP/j8ZVyAJu/F5oGpT0yiscWdWBZJf4nkvyP75qnYVYwkpBuhnr7f8fZ6bT/EDnDBir1eN+zi1REjDamn9iq6/FYHwjv3vCw5aLpE1YkcBokpBoFX6F+tWQd+lj0bqimdkPsmJAC6m17dzQ1bN+IdMPgq1BsnlylnXubX6M6PfsmXNTuAYop+P0g30m+5FElQX8YMCn3uLGX6d1DLyOADNL5BHIcFZervsNJ4XJTGj0gZx49GBEyQ3g4qAPtOtdtWHxTQgXyJAB0mm/OnevHnYkhSk8tPIYTHGKT0qVEO/nEyIc6q6ttNaFj7nEwaNj50lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwelwwhjLv951EXCpMcJpo0IGVP5H5puiSHrCnkVpqn1Kkw+1zhq1zStuxmfdypNHnCvErus+K9fHMAYGthRthBw==" - } - ] - }, - { - "header": { - "sequence": 4, - "previousBlockHash": "BB35CF6F245C5407CA73F847AC5487DF8C60F61348474D5D7F94683F2EEEDB8A", - "noteCommitment": { - "type": "Buffer", - "data": "base64:CHDdyjgaJ2slNneDkRbyaSqkBW5ijPjY9nHek+782D0=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:r57/zAhw8CpWeINyFnybI5UjARfD3hFhIHK8ZmIPGbg=" - }, - "target": "9233318228143625020618577701423519925017621426082203201059080050516648", - "randomness": "0", - "timestamp": 1717538702961, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 8, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAAdbblmP6WgplGVE9oz0WKWiH+g+IU+YvLlW2Ir1dowlWxZssjBm6KcJnDVMMyXvaIQrMpgAujtEXahv9iSq4K9SP4T8ZuQZ7CsOq3HHos5z+5lsGUgQI8rhYLy0nGu4qhGXKxxnMqWUN5C0jrehx6uijRjrZ2yz+ykTEh2e27HhUGg6mOyukbi925EFl6n7jnI1Ihl67wViryHrT2zcWBMGyk3LWpkEuGvPkxj3kUzPqozMxkyGc8ggHl3CpW9YXi+voM+oD/J8n5L1VBW2bHErYTKAO1wuZhU5uza+7ZjNPpuo67vr073h0hnnwu8aFCD4LZWE7ZJpO+ukY2GreV5q1tNPFYIBPn2Uk8ZHy8/imeQxu/Y8fj/7qFHEzoFdATeOYJb1kGaMW/wpLJKgQ5pKvn7pQJPdd24WxJWdKwS8nWkFRu+/d/cxwyWwdzLZyevMP7tXDaNOmp4LdPoTP0FNIUevUa99v4BZOZRBFGngc3RW5BDnWVWMjlP1O4qOq/erpGxvj9B+OjbxepuosUPjNaCVMduRI7/tJulGuQdMdzpJzYmQK+my41TLcBRJQlNyuqMHw6QpLaTTU5ilTUCpEOSuKCxXh1CQxcYwwbnWuexo2kkvHWMklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw1jbbfpY0TGw2ogdTk22dVss6hp/8pSYPV01t7zB0tNInXKQBj/BSEdqe/6UfH70iD4+/zW7l395Zb+FEkl+wDA==" - }, - { - "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAPD6mDMtouJiBFtRKIl3sieS/Wdx0hti1mnO+LmJWJ5q1f16S9kOPjPTme6WfZ3GHV8OWrt3mpsx9FEslQJddvC5J9Y8vWc0EwcKreCqdQPeYknY609MAHspJywh9hcRxFbES8l1EN4lA5OxAxixR+lwkObwhXfeLp4+VenZoeLwLtqJx4HUp7ZsX/h4qKskmilDwZnXaAChFw0jXfba7vvO47dRxPcs9JxochaNi40+zLsx85qMF6pjh0tN2pmJgNCBto4Yf7j4SrP0djw0xvJyYIPviKd/icdlLlGeS+CCZi/Hjz+xna8KBMniN3ORaHxCjWXQTFxoPrJE4Ny0/AJ1a/TTy938gLIK+EXM9QjIxmJtdd+FCGXJwkHom7ftLBQAAAE5z1IQcbD8btIqPHbqMde2kQn2izI0ffpmBxQKYV5N3nZmV8yZRYCGs3GiM3qDRG/mbv2LF63aK4GT/EN3/M1dXkCmSssZpwMobsiujqML2R1eECIMgggI3os1e45cUDbknNtxY3nX+AXBRna1DLpxXxbGI2MXM8CaZKMUcrnTmlTM8nuywtUZlgjVG7/KiOpc/rmG4dZisxQeMakvXR9AdQfBSAU+LfJ9ZNV6/pnUzZJKUHV7C0bV0yUGn6JuBmRW/JhbTPZcD3m3sl/947CTNN3CQ+aZeyK8mGNJ9UbLLFwWzIV2NuME/5Rzs8/bCrbf5VKEHgcgkqgwtFqI05bLOIIuzec+iSIxQtY4Jm8TlL40HndGOFGnwxTk6A00rBq188D/1hiTc8zErDTAwXtxNjeG8rOuvIwwk1FlwBnCGOU7XjVZFTRx/QM/a8eNf+H6A+iOcOo1mRLXm1aHHGwjfSqFXR1ehs6MXqSu/hjGsUk/LF3CDpDgkZNO4xfIfc0M80A03M4BtvPMGzcZxe4jIYkRRcLk0hNSbhM+EQpm+MpFTjFICtLLAMohPZqXL4oqjOJ1QVaki8UejkhOMTqTztzqAK2VBfWI0Kium49axlipAdHm1+qEVxxHatDhriQRoAcPztq8gCrhetjY+/Td6AjZsGXdYkLWdIn05st/Es+XYBsMEj69R33XmPwarwpsIc9BJt3qUkzc3CJ3fODKgAVvCBiSenJw5smwNlVX/jqoQU5gxvXztmYGXaUS85Aa78m5yuSHocyitTxBDnqBsI7l4eGGLHXP9rrZ/3iScjdf1ZaogSUWmA4owB6cGkR4areyP1+cUbf55RuPbToE/9TLMCLS7EY/R1o6G+aCzOLQeYB5jaU2hbuyqH/7QOjxPGgLVU+JT2ddU9imRFoDkshoQL0mY/BPxpcnZt1XbNZbp1gm88FQYV2zQb2spsjhw+VLRK5bdO7AuMyRuKoTuIVKDavcvDWQsLY50J21pDqW0d5vCRjSzUawKHEFc4NaBGL7GInv41XUIYCpmBtTTzPfPD+r8QrNxQRF29PSnHNipmsjVJS32qaY2Z7zc57C+awUe6A6gwLMbAjtmhtyVfjAsIlArVxUnEDsDwdRBeOCzPwVrkJ3CfujFNBxhrvIkba/fuXtlYZHiVcV275f3Jyhr9mJisFy0A7of7TVCdCGJ0Ou5F8lDMG35Wf7DlGJmYKLFfY8IcQFUMHkTLWDMMjrrVq+QUf8bd78zINjiAw07AvkpHM00a38EmHK1/bbRiz8SEHdY/7no2yI/KC/+T+Ux+yMakkFulLtdfh6+uX9AwvtOaM/170YvobYlp2wo3D/U9Dw5Jz/fmgjfHO7TolzhWEx+Fs3RL9uBxT7+Fpo0PeFI754i2+39Oo8qitirtDoHtpXkDevVWi6VeZp9pohu/H+u19EuEvxadllLnIahkVSwNMAoPSl7H+bAZJCjGH1gnqI9B7Z0nx076cX17BPG/VnKwnRubKvv5F1bae7eBkk7uMeo7hIthg6AlYJZyLSbMT/8DO0U7HdgrBE5EGlwGn9zscLDNPZuORy/5X7D4N2xy928iMicOT8oCw==" - } - ] } ], - "Wallet connectBlock should update the account unconfirmed balance": [ + "Wallet resetAccount should set the reset account head to null": [ { "value": { "version": 4, - "id": "e6e07578-1ad4-466c-93f6-d4e2c086ca46", + "id": "4eec859d-9c89-4908-a23c-4cded4b294c2", "name": "a", - "spendingKey": "420843092f654880ea9945307acd50f4a5cc81dae58f0886f1f18ce25c1d8d32", - "viewKey": "41b69bf61143f22ef34c11b53eb201f7489e1ee838f2944685e45aff0f13044ec36043c8516e8ac93133f1d7d2e6c62886d5787f89bb366abbe265efa64423eb", - "incomingViewKey": "14afb756bd4dab75ea541ca2395a5a43cc7c2d28d2604c9e2db33dc8a6b54a05", - "outgoingViewKey": "2f8bf7b9a052db1c5fcb25b85ad93523fc865c9493a12889dafeec82e1c98ba5", - "publicAddress": "2db05c91321a5e96af2303c9c9164bfc0a2789ccce25bbeb4213cc986fa755b9", + "spendingKey": "361eaae46d35035ae781a36b9e340258fd5afc2c9d3ebb24a80bd62303578b87", + "viewKey": "42208315b537e8d625a3c2ae5b77185c6b2cee1552233c7edd1a367ddfb39a54ccbde3760a337020c7ed42a0e3c75911ce6a10eab5de52551a445170db68f507", + "incomingViewKey": "169016b8098a97c18599273861c150e2232c7ea5bc93189d0eb076405920e807", + "outgoingViewKey": "ea8bb7fb76575ec9e5c3f5ab3ed106882d99b9b7fcc9afc224e0ce595e3f52f2", + "publicAddress": "8336fb0657aa4cd82cfb5fb4b6723300af8d8cd64f86b14ece8b0230c6c807a4", "createdAt": { "hash": { "type": "Buffer", @@ -4628,7 +4498,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "ce1df7be9844e830b02a603d25146aeb1cd88d47b28065115d8701dfb9927406" + "proofAuthorizingKey": "42a275b3cb8c60e9b90cc9e4d8ae72487cd3af1eff1dd61ec1b2cda588ce460b" }, "head": { "hash": { @@ -4637,17 +4507,19 @@ }, "sequence": 1 } - }, + } + ], + "Wallet resetAccount should mark the account for deletion": [ { "value": { "version": 4, - "id": "ba507d1f-eaa1-43b5-a3d5-e3f0835b2163", - "name": "b", - "spendingKey": "ca3241bdf3377026987473ae97f74ea32fac0a76bd7afbe5aa099a1d5eee3737", - "viewKey": "4a3540f5145e4781a31d65cc17b2ff8a807a1d4b565b854d9bf88d17a5215f5898807830f904d96c28fe93ea9137dca15ac7a84101c0eebf8854103f934d1cf0", - "incomingViewKey": "e3fd9ad198d5d57eb446d439862b8e4b529c5e186999aaf5319b0c6043205302", - "outgoingViewKey": "fd5aefcb77a84360379cb42ca1575af92af0fef6f9d585e00671788f5ab7fb69", - "publicAddress": "1bc946448310c4b70c9a0ad476741a0b0afeaf02750bdb5ca324ceb3c743dce8", + "id": "1db0d7c0-5dc0-4af1-9349-6dfdd1eec19a", + "name": "a", + "spendingKey": "d380f4af79c40738657e0ee8007a4759f9486df02022273d48e2fa9cd6576563", + "viewKey": "7d408b8858c58099450f0ea14ad0e810064ff08a73078617753eb29d30b7854aea22a9f5ed8612759086087d7b78b3aa50913829f494820a533ce5f13ddc7291", + "incomingViewKey": "f16815402bf70f6e6ef3b167a3a940419db3d05f8b6eb56cff9d09373bef4900", + "outgoingViewKey": "92a38016572d0afc6a1c7bb92632134c761b4ef0dc82e6083b61c8ffd5a7c1cd", + "publicAddress": "2b617eadeb40c7e6679212318210dd34de44ae591cbb1245a80563bade2e9706", "createdAt": { "hash": { "type": "Buffer", @@ -4656,7 +4528,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "12baa42c0cac30b9bef37d863528b3d2f6005299c8f545bf7bc219f4b7792904" + "proofAuthorizingKey": "c35690ea9aedc88905087aee7b51edb296caa78169107d016a7c2e785676bc0b" }, "head": { "hash": { @@ -4665,75 +4537,19 @@ }, "sequence": 1 } - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:E2EZVHjeLvWcAa8IT7Ni2Ux80EXU834VWaIr3z532nM=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:JdGMfWzvdPdPqLzGnPg9qbyxaaRTn78az489TPlJ1Kc=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538708585, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAgSWTdzyDmJymRS92zJ6fgFZXXdCw35XCWCRH7lIZpQOi/86a8KBnJAuMLY459E7oJaWl0uBXAtbvdworkuYoqsYWdX1miS2mq/GdiKy4srWWb8E2TM7twOJ5bb1wGk5x5wr8Eu3NvI/SaWujCe9yHptG3CPgENEt2jNdfDJI/nUCi8Xa3VewEt58G66eRKPwxpmEhQGTfFOw7HhO9Uat+8Rp8ZPJtEkKZnQy5ABMPICUDiTYnn8I/XX6nUFYwguKfotH9L2k1E59oIod3+8hSj2lTMmu5mLi9L/U6tkLAJdmtVUuLYiGKJE/F/PS/50gRl/U72Bjn0D68pPWyX38zuzJ3FcjnWgbIdeMLgGp7m2aZH06hNBCjFWfI3D/IpIkPJsK98MQ94gzN0lUXkNy+c2o19MOZlWcTfg8GRRjN7DCEav+1Qgqn6LnyZMJ9KPFnOGEJs3fd6UllhUiWiAHAcSQnq8b0ndA52LelRK/2qpVJCKONCoiW9GS4KNReZD8S6bKMusBUSESKIn7Xbz60zzOFpCi/yJVqRsjZwG+QPjSiGy6lsFiofURqGFtWpDtz31uvkhB5zvs+DBwMqxED46GLuP7DrQgZK5y2pqDGNg7Mf4hLgkgpklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw2cQJDVa+FJ7fx5sNcgq4p9gC9CqEPIpmNnUmPaBLHZp2Ge+G4csJGsjeKEcKyBCTslLzmuLNXHlMi25iEb8HBA==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "42DBCBB13E03695E976497A2432B740A14864A5C55AEC55A756C2AF0D19ACFEB", - "noteCommitment": { - "type": "Buffer", - "data": "base64:HOIPwfKSv4AOyIExevqAx1L2fz/yxsgMfKpg8rjYpGo=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:6iEUo3N+XYPWM49f6kf6gljcIUjIkPmhjUsVWsBYbuw=" - }, - "target": "9260366780148527510972123832573278885902566341756516011968728852484845", - "randomness": "0", - "timestamp": 1717538719107, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 7, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAACZUa6EoIZyCC8ZqizUMtc1BG88YzWcApg7Xrlj3+yQSBPyB34rE7MvJb8f6VfQw18feDEV9FG+5ZWcnmYD14uotAMdtUPi26Kzw6ZO23OQeKYW3sjRVagdVQquvPXgshh3rBWEj7IcrcjA1ZCVYaGtn0bJVuL00YhWIzN45VgHcLFYPwLnFdVI/ALyqXtQ/BfWU1cU2RXf9CAYYMZUL5f/qWk7HgpaMvdYv2RJjJK6W4rJ9f1LdBtcFOizPRgfMwgUTqp+W5Izq5cViYhSUNbHRd4Xgy8m9Awsnrtxe33yDCERXj1zlEnZmNBG6tQ/GwOhYlJiberemmvVzl2/uVaxRgr3CnaizVdPRleCIb7xrEmlkmWnSSGVEWZDBALgRVyoopZaZyrKZKLJrPXXuazLFVWeax96pvoU2fer7oQz1rYdTKDw7zfSPNnQGvVCuqbwYmz3lqHimBHnuJg32RGszxK3NusII1lsrINTaRPK8NTDm30Rw0ZtzjRT5gcUKE62DybkU5m3r9cKMoMF9vq2IpXUmJlqjn0tTUf5GFoqyFxmGyiHfHZrUvI42aLpWqZRzSMp4Gv1Pi9xj5KILu69Xh++gYYH5iK61dROhLX40jH+HBFnRqJElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwo7KcKsMXQ0jYoWFUAeEweCvg9iJEVCanTe5cMzboRzDR+meyxr2aJtHviqQz8NOO/LcNlrHPH0Xg9Bcc8hFQCQ==" - }, - { - "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAtAAY8ThmuhqcQpUf0TphkomKQMN0eVjzwtGyMu+mZLWSpVb+gyh2ydR8wSopy6sh0sKHPiKwJHhDB70UCebhPPnJN0yvHuBqVbcmiqRBZYKI32O0Ovjvhvy/YKEBwEUFPQCwz5gllRoS8BVkbXJ5VGNtziCbldMaLCCQ1y9GhesNGbpsFbgADzV5WODkNb8WHjUlI8hQrgo9W5vEXuoN9f+ODpR1upViALfNthtosZqw/WnWHOZ5Q3GON6Z83brEUVT6m77LXuf/qo2x1h6U9RTcQQemOc6m9EXVV2isKtPdpCBi+P9unntLAd0ssYNyENruCC4PaEVKBTQ9D7vk2hNhGVR43i71nAGvCE+zYtlMfNBF1PN+FVmiK98+d9pzBAAAAL0kFmFW7Cb+uCgPMgQx49tlUgAh8EpgyV1zCErS3MC5mbMHSfOtEYS7cZUHucyJdu6X6eBXM4/S+DHUipaw5LWzSZcrglSdOOxp6nx0wylmm2y416rBGMNj0eXDjDl+C5OeC5vDEEMozWFMNnjrMiuE5XCao8s/tqpXW4fjdsXdAabGhemDS/Oa5hTaQccy25R70MiVre76bb63shAvJfoY2t82tIWgigvyK6dDsAoNgFemsIHo1QlDfTS2VbY+lxKsJo2CEu+n8LT1BTV3shPRgG5u0wfYaXhdx7s38LYwiTk93UKKVAynpVa57xHubbDgoyirc2Y9Ofk9iaC8Otk7ZxUP0gtkrQ+HXY7JJrOwsbSxRPVSGk7yqPsiFJz2d1vaOAlKaMLa4Gy9ADgxZP8rUmDcuX/rDBThiow31vWNyAr/rKbknYx0GQsK55MQqb5mq/lsezaXfftElNrSvz0pbmLyYCIR08kq8ruMBOZrbQPPte/YclPgILGNnMbu5EacoOxSIZEJ6J7BjjdgEZVE4d2mNo82sSA48JG0+yVXjMaSTvrALvckR7t4cV8WoW4fqFkResflba6+ewuENd/+OipGEF3It/hROoWnM9j3mQYWO41/FHNu7SWdz4ZGiuMBgtykOXkUpPzy5bqmvGqaIl66TJkOg2y0jR72E1WhNCqZatuCIVBdgE+DajUGVKnCc5QPdLy4u3aGkJu68Wo5y4WJMxceqwnghLXAHW6qxTSjBPtgnZ1zmYSiCepNVDz+s/QD+QJR7ar4ZL0H2xppUIa5sYHSiQFalM8dcMP3OZAtPoixbjSlMqMzzT8eP2f/HH/PG9/C7HXdcZoWnZGRee4buHHyAXsZ6Z6PubfVoJ5HcuqsirSifZwvtyHx9S8X4OF+OCSLZXjg3p7jLm/prXSMqGMqWLVX1lhnTonx7xL76QmriFQUcJR83hTDgGS4YC5k78s3PW+19D0GpvhayRGyGzRkn9PteI9kGZxZFtO5Oj8cYsegjjEtHqrOY53nE6+zpW7n6hW1DxnJxmW3mbryePCYeFe052oRLU/kezlckEj+RASDcUGYQ7MWgIgitGWlNXZb1KKy7ybWlw28r6nrEoUerMo+fmwCelciMMFTtkGa5gRRIcDBKw1qLze8D0e0nWkyfvbmpY3tPjYdQjDvfCQGgx32FqeMiRjKJLGdFf2USh07LXH6bj5EOIsIXNpyZcp85qm98/WUqMW0Y4Iwrp/DlJreJRNnY+U9QBNNkUC61f1MDf5VzXaida9Z/Y2pcPSZQQjy7fhU1QZQNSaOtQXsNaJ5stO+47Cm0K55NYUW1/ApYz8Xplty5v/8SK9s5mYeFR+mZeBINqxKfy8Rf/L9MticP2HKH/Mj3n6pUH5FA+QYECkMsaChZPM39u8/wziWeJ+VL1lnEX6VQvHjXtra9R+uRDJod6L8OLsa83jI+ZOvaxZyQJPTAAKAeLh2hWGIG9SC0b6uwMB4pKJIdLUOXaBQaswpFnURV6EHxmcZqHMBXvGTQ7zrXDw8AAVFx4S7pcWEssEJBGl6iXtfvQykxIk6WSH5OlQDT2UeNQJ35FvGmhjNMSZUAw==" - } - ] } ], - "Wallet connectBlock should not connect blocks behind the account head": [ + "Wallet resetAccount should optionally set createdAt to null": [ { "value": { "version": 4, - "id": "549c0c4e-62db-48b9-bf31-17ffe7a8e31b", + "id": "7beffcb8-b04c-4563-a719-631d6e742547", "name": "a", - "spendingKey": "38392b27d4f07a540dc60359f1776dd0deff50f2b8fd2acef76e7a0330c7e5f2", - "viewKey": "e118d06bd3e2ef6c7a3eb2276e71bf920e048ac57f9b8278c116f0682a1075e90a52c2d2f3b022d12788c9c027525cc779c79bb0b593bc158d285767dc9b3128", - "incomingViewKey": "904fc0c2542b4054697441a9aa09651ca4d23f87e2b009ded9ce9c25f86e0602", - "outgoingViewKey": "ead9f6e370a250d3e6f6a8bd26aeec8832f31fb29bbfde24bf29154c3963bfe7", - "publicAddress": "3c91103dedc461acc29cf0f2c38ea8b68cae02594cd61f007418ba4ea91e0b4b", + "spendingKey": "d02703f5b2ce1d0e9fec295b9ebdc26abf840553564c7c6c82741f7f85be236f", + "viewKey": "8dfcd122af2b54d80db4593bb9c01c01a4b2dd0552907dd93c6bee790429905e18d941069116640a12c94d619f770d43b2267174b23ff5557d57c396aad28b9d", + "incomingViewKey": "f35802b507f06f6c9f0bbeb9875da6d290b147a0586c7cb83e341c120a005c04", + "outgoingViewKey": "0f418c817a72226b81e89cd2bc1166bb593d6469aadfb758301a5972a99f849e", + "publicAddress": "98648a14194cd07bc1fa21d34bdb15fd41d556250dcc10f35888f24191cab41c", "createdAt": { "hash": { "type": "Buffer", @@ -4742,7 +4558,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "d9abfb17706f10f70a61c41f88d4a3e931835f3c98912e20edfcddc0b567370e" + "proofAuthorizingKey": "6f473605fc3fab9a66ea0f715ad6aeb36d624045086bd6c442497af0d9771103" }, "head": { "hash": { @@ -4758,15 +4574,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:9kj6mLUHK/z49LhREuV654QfldZes68IGprirmjS3XE=" + "data": "base64:xxBl7uwenihtMyNth1+es8eijtbg0APwhjU5JCkEXmI=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:ewxaYXo1/0UE/FqcSt3F/t3vxfBtF/I0qsZjjw2yiag=" + "data": "base64:0AZsQZxjyOjkBfy5wcV0LvCB407iMYL/C86Rx7pxIgY=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538722947, + "timestamp": 1717538924255, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -4774,1042 +4590,50 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAityQygHbqt0VjDxEzaFyBVzuioP/pzHMKsNiBhmLYjyBS4p2xk9YpIcbcrPGcFMuUCpdBpq4YzcH4AkSRu0tEzBB8Dh78NnQS2Lk0ETaCaKmomParGghvWIKx/S0B7YIOP5z4exXCqEVty0fymVVpxJ4EBghJhUfKwmuaaJ/VMIYkjAye/mqpgkAKXHqVN1892zovFr/+akH4xkKcdKUPNG91oLTsoHil8TzSMxuRRu4sENH/wA3Y4Yo6o3fuVplXQBGIvC5QvxJBJXPxULMH+0uBxsVx5syITQNutdEjaReDQ3rdgpHH3TWsOGzvmBRLw3es4+dRoxGKNDiYLU7AD31T+iQP5JqiS6zRzShpstX/wzCCqdnT7FN4LjG8WctMOED+mPkCPeh1QQfjkGCA6z8Lr4bn0ihy4eIPAq4fCNwexW/WGwQS3GB/es4Ruo3Yl7lgZho4f7xtSQ/x6qFp9yhGxlTfWpGgJKX1qqmLMrJITVH4UWTqj1LkhHwYi1WFaeovH5K1ZiTwSrlnZQY9bhtc/1MoVMjaI4WXDCW3TW8pSx1q+KkfYzj9D+A2ccteQ3wJkaVGYvyD9y/i22QYwhLdhZ45NX8HM1ELq96n3vCz2tbAIX2PUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwWa5z+Y5ScqK439M9t6NQMoGwtl44CEdDcAgpq/v1xG7/IvyqH64i4aCd0oOj314uNDGBnh1i386kv+H1IVEdAQ==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "F061890B105D00F09F2D22D79916B8915E63660D4F8613DED1003E1626F3554C", - "noteCommitment": { - "type": "Buffer", - "data": "base64:we4LwRNXxKAUFs9XFZpYKZZ5lrVuY6Knfoa/mf2JYjk=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:A0rjOI1sM2RqCm7hRI/ig82QVItLkR124lCsen0kF/Q=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538725161, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA1Qt/MhtPOUbbH1Dq5Nwe5NYf5mgOF9qahSi+qXgQbCaU6LsxbSB3o9/imSy10i1TSmTchg6ks5Rp9fNtrGMfGwa1P5RpUECCJfvdNP5/DPuI3eNktr8KbgJcATJbTHnxs02dunV9Ai/lCEJXIFjxGV8kecG3kfwRWQSGdwGf1X0QenJ6mU/cQAHE3HA/LJa9cdG/y8LwUuZU0PvKq3yLpjufC0DYkQTJJYsffwSDQ82u70eGfQ+Z3wy8UCHYP1/2rBg8wFvQ81KGwtKOM/09uf3cFF86wJ8ySW9R7ys/bicUeoQJBP/FOi7GOgtRJw6wwuv9C5qwsLPau8nOfQIVG32PhpPX/Idzgw8R32Enuj7rvukdT3/9nccHL/HYpb8G/iEp8ajRMkUluuRP30/GHjhMxuP8Rb7rt2fsY/GZTV7xb5Rx6+RQ1zFmkyeIq781DzfhujdO+xF6iqOk7QROcYPK2PnZdmcoEZ4r8TO4aEm1qcI4eRDH79OBP73/60cJJ7LkKw+hFdq+lkqIq0SQZ1bkSEtZpw4RclWfELY6vUg9xaciDa8xWbmMGLVTo5oVNLmv0YBz6RlxmEH98piaznqd6XCuexTl0EYVZ0r/zb4cOUuHuciRr0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw0xRvTW9fTJlsGdjoJU/AfEzHYSusIArbgU1x2eFhDJ3HuGPuWQxfjtKfdzOJBTlEzoU+OeEiLwfWwjqNE1yRAg==" - } - ] - } - ], - "Wallet connectBlock should not connect blocks equal to the account head": [ - { - "value": { - "version": 4, - "id": "876ad5f8-678a-4b18-a7a4-bed4748da70a", - "name": "a", - "spendingKey": "2cf20f13a0e93c09e06e8edce5c61eac5b4750afc68810661dcef81bb513f030", - "viewKey": "6a7590f9e9ebfef17356dc80f13812be8590f42e619149e3a139aef0e19f7dd9915255079068983b47871df78020a0f2027790b5a959794fd37cac0b4aefe0a9", - "incomingViewKey": "2de5acbe99b326bae9267b2471d58ede40cec3138fe156599d599b1403d4a107", - "outgoingViewKey": "eb45a5a3b4a9cbb1cfe22c1efea45c29e55e1c83d925d3ca892d693b28681235", - "publicAddress": "580336448b2097ac9f598d4aa6e7300219c49bb861bf11c24a9278ae65934f46", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "99c3f6bed5f5e73d8c291e95c70056f43dada58594cba4739e833e882301ca04" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:PANqKX16ed46OhXoTdcTbh109NRTEL+0nkdSUSNcr2A=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:2tlanU+nqFhfh+6GDGPHfRQrducErz4W+vk/nOeYhbg=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538728400, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAvy5s0E6Tr8GHS9Mv3Su1NfDF4q5kJwm0Qr0BxrGU/mWN8UY7Ga1iEFfUCpkc34j1lhNrUqG0dQM57Uc1OkJAs4oK3sASDF9xWwFwaCCn8GqXqnZKRdKjCB/elDjYeRQP58TOvPB32nxH3SeWIO5/zaUWY5swnUoXhhVsiZ1hOO0K5Qiv+XIcvmt1sUMUHstmB/MaWCX/bphyphh8vkYQpq25liVFkq83a7CmFwhv/7KHEwO/fmxrE7MGp3huWnv4Ovwl/DncOZBKP24rovGWhv+SipmE8eneGy+LONh1wRf8PJwP4b6QIR33nkquWibbnIJgstjYAlk2dCmfXwUTYMfLKJrnsEfst9NvhS4jOse8sGCDjZotqLif0koQTNIblY9H5cEwZsJypurz2xTbBeFgn90E9zl835tF8Ky1RtnUAWMZrTmJG7FkQWnIOVesMmOASe2HfuUVrQa8AC5JQjFlQmmFsMm69wL9vlfKyh5eKBGVjzviBT/vHd+zod8HpkaDL4eW0pb7FxxG/ahom49ohhYTl9PojKw/q6pb00K68i6lX286HGcFwH4K3bYsJ4cPsu7FWBf3YGVxdkGhqpdhlKsDTDh3IUE9+IbEliEk/UiQKwDqSklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwutUnEeOJzCTl+ddY8OWJjeu7mUaGRv7ZQlOzZXX+x1yrHthBe360JdCB3w4e8sYzVc16+exatwibwxJ5bUKBBQ==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "38E4839FA33029B096650E9CEDBAEC98B5B488AEDBB8A4D809AAE96E9863BE54", - "noteCommitment": { - "type": "Buffer", - "data": "base64:t/xu3Ock79Ta12+GZid3cxt0ACVSLDWvXeF0PgbLrWU=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:u85uO2zQwGWgLkAunfsf3ZPYU6VoD9Rsu6K05NJWl6E=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538730986, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAxrX3vg1YM1H3yTQvlQT6ji/CdaNebbYYdDfVbug9ybaPChtHD8CQkHfo739PmAZsBDzoaf7Pv/i4SwG2tpE//aYd63qUqcLrlKZ1c/DhR+6k3J8U6zRdND01JrkK+M+dXQtn/1dkteNGDLP4LOkWNQptPbMgDScW4UhxPFb8PzwUpijs7ccxeuCiXvmauk7K0T5FKgCK3q0HQNhj4vzM7osVbzz/t9kMGTSCNdDkq1+1JIzal68440UnIa1DaC6nrBDwvsNOYsl7iVD7QFHzYK9RD9WMbZCkOueAalN72dTno7UJXmnkC9vgaU7HZFKJEUoJ75aWxpuhruwb2aHpa2qZ5wISF8vN7CJOkI27IfZ0FFJqoDAUt9xLPt4UH/RbqNJX0XrORlwYQQjF8Q3JJjxYG2aJlnuKS+dCeeHGhD+71zFQvjYkX+wnTZk8Lqu3oJXO6InzHB5BvYyOc3B3f3a1xtpA6SS+gwZdzI8JGHPBiMOkaNNzOBS46ljmWFdKUu24FYO4LTZC1r5/ArwpByOf+ZR2aarBg3bqhqBXdUHejb5wAaFunYyWDcMdvHe5/SYARnn2tcp+ED6cP1wc6zpf0iqMLQ0UsEdUwwxXw3czGM9kxhDUNUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwf4WhCcmuXWYQgLSaI6PED2JFVKnTwOsas7wTdJ24ed7dlXvuyi8JfDraJaGvgUftHODqBn97MyjIuphUh7GfDA==" - } - ] - } - ], - "Wallet connectBlock should not connect blocks more than one block ahead of the account head": [ - { - "value": { - "version": 4, - "id": "52f3f178-cd61-4fb6-bec4-152614086521", - "name": "a", - "spendingKey": "c55214d79107e45db306830b34d5d2bcff54786820a5983ac32d6650f907f49f", - "viewKey": "bc12bfbf43fbb6931755d06e7568f1e49b46b2ef45db44f47d463e1635879aa97564c0970b1f81018303ec2550bacddbd41f4bacbc18c04f9d76eb641811efb6", - "incomingViewKey": "6914a8b8060ebd61f489938c8f76f5205cf2c5f462599de32b9656a237457605", - "outgoingViewKey": "3ce0a0bddfe06268060f00fe2baa4a5ad48cacade1cf0baca6b0df0e1d456f37", - "publicAddress": "d7a73845f559cfe20c840c3e944a439b3c6d89bfe93aaab4f4f6062d69a033d2", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "690371ad8dd629b94449613b54e6715962d573b21a7e39e772167e575dc6fb09" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:axaDsNs2wjUhByGtFxDUW+v5qC3s9gFC5h7FR2TJz0s=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:25ZW6iiZBiJjHP9FBDaXBr8tBIrPAPxAGxPhNzaAjS4=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538733868, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA7hY/V1iRONzfsyFwdBgejDxV9sePkuGCSkjpnB35oaqyYuahqgCazfU6XWV0Bqr4gGvpx9h81s0BbxEJlkRnaxkbKlcEFVnN9cZ0JXQvrTeUbPqT3Si2ug9zj1nx05J9WebDGK4bByqHH8zSlfCq9vM97PJxq59Y8C9a8UjM3bwX+URjOZBwCrtJXRTXEq8WPxKhK9SoMc4nX3bgJmPXUcetuiZtpTRUYR1zGJO/pSqQd9XFiiJMKdUgsUZZW7CFgjRBlwO9ek7cRFcErvwFAharpOg1J+Ty5lms2woPCO16BH9Zo6DLmn5n//fJcPJ41ol2PxtzI5XGh/Te5UJTK4HEh/+Rsiboqzg7a//cjV57yWERern6+/BaA3z+/hRDlsNcryair7Z444tfhWmhJ0ha0lKfR3o2iA3ivli+1lCDVevavXlAEZH34m/wvWGdovOBZJaneUeNFTJ86MjRSI6f6JyTlHuvhMUOcoFxhrAFgzywXuXw8zmux1tGlfv8qZxmAqe/vfC5G5BOvumzACXIl9Coepx1QyUHxt2+U63boaFUPYtrOmO86e8ia+muFlOsPXwNHvvYi2EFVfgXoOAFZrbMv1VKpgqxdc/uAvRQhhl3gLo4yUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw8t9NBU/LZZLTk0+ukiOf99gcMMwfxDuJLucKn4VfvcPCc5xmrtlUnAwACFflSpWPoQMQqCcnw+kplNwyriQUDA==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "8DA8A4399285E2876D74DC2DFF1649DEFDC0D47700229E473D0CD02308613D58", - "noteCommitment": { - "type": "Buffer", - "data": "base64:7ghr1z9O7JdSQ2GcoFgmk7//hVvET5sbM5VrhP6wATI=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:ImH2S0om5NQNg2+2whXnhJgHannGzW/3cNBQA3Tw0GY=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538735542, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAyHRH30y2J54NKCqZPpQ09/jnfDD589NExzehSdtNljq2eVCXDZISUrLOoowNfpqmIZQDWfeH1ECHE7htZ2kobY92l+TjtVZjZSAhu0I20/2CKQghklwUPP7t31s12QnGdaLnTX77arFjdCotgQJj5AvnLUiBvtDqyE07te66OR4Gd0AWeLLYAm80gu9YnD6uRUldDkW/SeWXSvwphhq8kqs4lnsJGwozluBg1kOsFNKOt43w812CaEkA9c2uoalwyqMKuFO/CzzWapQk6msw07DTXZVZw164e3km+jYn2o7ZSpRvi2Pp7wJX+6ElRmRqkXs8JswuqTiwOytkLDogSHAYnz1QkkdyC1cxo3GX2zK1oHEaBMvI/lxGiQIzLgpCuEDFmch8X+ni6FmKFU6LbSB70qZYIBkiCP0YDPHbmhkvTUMemgc5exC7MrJMUmKCWRBbYXgC+T59v1EY3MJJAQotn6ADKjZxLdUodmA/Pvj39cD1gpslGfZO/hSwWI7ObV1FlVxf8PAQBurJogTvwaVBVMgsqD8G4VyWMjlXZSIDAcnvkFSKzsJf6MB3iQ8j7J9Ea3SNIlziX5ZB+UNJICCoM6plvcwR1q013zXIWTYuDisbs7yZKklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwoCxJ8lTQSddh+aRtx8Y4MrqZSvGfGlxRFCNlYEGltlhLEGhdOr7RL3vBHTtEDxI3WVG/GxEMv+CBB0nlV+v9Bg==" - } - ] - }, - { - "header": { - "sequence": 4, - "previousBlockHash": "1AF26A4624BDB9B504FD50692CDAC15B70DF2B6D618C555BD418B485078FB0D3", - "noteCommitment": { - "type": "Buffer", - "data": "base64:7JnKBObZW9pCkqZBeU56zV2LkIaz2oCnZcW4aiXoOj0=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:ZL6WUrM44Uwp3dJuMsZh6YQXWrK8F9gQnPge0yAy+qE=" - }, - "target": "9228823284279306817296266184515742822248210830185427859262273659833347", - "randomness": "0", - "timestamp": 1717538737319, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 6, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAvAQGidZtCdZYMY4HRsXMevvLIrm2v638Zq4wO2dAHOGonRQA6LBOD2KdoFixWAUYCRqtkjpIp/IqkuuQvJaS23ZaiPV2F3sQqRKBrQtnfXWmm559+q7TjwACA78K0+b+UFkncqOkZb5KF6eZ34aXkFBRQMvhRPzuthPnRcT5JpURxA/uLxwQcNLfTJJpZ02n2A0voCXogCHEt2Z0yi1PX2rvSso+rvFUGB1wGn3yu4SwFFclz20Gcpxme5k2oGCJo8Zow25zA69N7p3vvOxh3pf8qMkCt1cMV8HFpvV6kiYSCRNN5rt03Saf/O+yUrjdXO+8hiIJ1uOCSI/7fYkY4FVuqFz8ByNvYrJ4iW9XGXWu8yU/x7OUJAxY9TlBMIhZdWQL1s3cvNWdg0RCP08c+xVLwEr8e3aS9kxkSzHV04DzdKiTzrAHlZbspNbmCB74Ds5UgGVacmmtwCoytaTc3v/0+WePimy+ZdpC1MGAbg79PhATyHQ3DZ/agawTrRaCuRISHOJGW3lCmIKjy7CY6a82JLVy0Qlqmub6YJgMtbMkSrP7+W6yo05OKn+AGetWQ6H2ByXJDLV7rAhX18IwPf3TzksD5BTPNzFkKfDwNaX60SadZ9qbYUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwkw+TgO7gc74gv7XXhB5vi1jJ8SDnTY/wL0j8h9oP7z3XC6+6if+ob4fCr6saUqSSBMaki26UzSlLZrkhIklUAg==" - } - ] - } - ], - "Wallet connectBlock should update balance hash and sequence for each block": [ - { - "value": { - "version": 4, - "id": "86d20122-a7aa-471d-bec7-095455465420", - "name": "a", - "spendingKey": "545fb9021b6cfadf2fd90b94c60b879e34981ecb13647b41bb831a559ba5a17d", - "viewKey": "184b832ca1f4c996130dbb9dc3b58219ef9fb02e19b8180886287fb165a39051b215287f945f58ff8118300fabccac109d2318c2cddccb0b2938677dce3e2e9c", - "incomingViewKey": "e9ecd4dd525a226e840dd9a524ee807709b1220068b1e502409fa32799ee9c04", - "outgoingViewKey": "31eb8cad991687c2132c3fc08be99c62296423e26d4afffcda306ca9fbe658ce", - "publicAddress": "dd644113f8e1fc1d324f34060696d8352e97a17edf0a3a78dd54e1d246f1a6e0", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "a6ecfe0bcd13494281d7b3c00288ce5c6141d3948cc0c888bec52ac3d187c50c" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, - { - "value": { - "version": 4, - "id": "2675479b-fba2-476c-8293-696a1076290d", - "name": "b", - "spendingKey": "357f8ed6d40908876618b6c9a88c21da09f8ee02da5b78bc30a49f0eb3041b16", - "viewKey": "0eb7686fe87898e2f3fe20e62e318e46d80d21e22e313ef83d0d760c7d8d4dd23774ddce0610f05d66584a7ac28bb8df1080c9f4a6eb47a75d7d2d8fd3e02841", - "incomingViewKey": "768e419a67825b80983a529df92f94e16db370f6a78965959c6c7cdef0471101", - "outgoingViewKey": "3835258a93bbc9f8273c4b67d74f47a3b70a6200b1be82f2ef61d97ab09a492f", - "publicAddress": "340d2b8c66eaefd69d9ed97d9e047903e9693f6223ac370da7f3ba3c87b7cfa9", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "f942c2ba94cf975969ab70c6d62a2ef0bb097a75c09bba385ae2beed16fdee08" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:0cTKMW7m5/wqFGYfjkZddvkDcuZpR+4iTi7G0v4Ogk0=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:HK9zcLEUrSzUEd+9ClvZUbo/Yd2PBb0ir18/7NUx6Fg=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538741136, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAADxmLDI56+zX4HxzL/zq0JsCDhvaW/2vXU9L/XpDXXjylyp/BSzy5keWndulO4sLs6ESN2e+fCHch7yZeNJbm8Y7y3o413m+M5CtZxstVbiqV6DBEqPBElk0UflMYOqIH9GPnk7kohtOJG+PFo/TKQG9AocBa3NmBEM5pItzuad0SofmETSmpXbck3eO6cI1VD7xP3pxEfVUPxMvouxcY16z9jQ2mRttVgN5+AtwBPCSM9BI+aOcMthwLDBrkQ/LFzQ9RSrE3+0sizjvpv2xhuHeyyzNktl0V2bMm/BXkEdPVsMvrIzzgNN/WibMzeh7ehkgpgxbiImz55NJ3JqCRlyqkcJ3bz4qZ6uSlKlD+3Hbrlt3Dwuee39JN1bUqWtw8cIjXEzmXFLftwvuS6mlTDEqxUBGuhiiHw6+9aB5wv/O6vbxb/10oXsOXZCzgpFkPRlzKG1MPcgZehOkaBhteQU9TATcvz0aPQCDXsxVxjb8JmsrdllvjE1JhRGPyQXAdetHmVxdnmBBe4oG5RY80G2wu3qY3q9lXX2joYreTG9MHmcW8n+0ZfrvQZyD/eoi3Z6qskROAnOT6O3OeBdxk5lDkIRMJ756JDNPW2/3vGOV1gYcz3j10qklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwvXrjqeHuZLgAvok01nDw5J5WtniafAoXKYkSeB97T+pRy9GdT+WuhWx3JxWSCz3J9XQhB7x5lcObKi1YZWZ6Bg==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "6BE851616D0334EBA8E769E8192773A6DF7C8551898346777328FFC18D57939F", - "noteCommitment": { - "type": "Buffer", - "data": "base64:u3Qbqt8zygeR+feBF4gGS8gXTR4Xkgqe78RTy+XMSyI=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:LRCNWMKthGRqlN2eVdXY6fLgJBlgkoxVdUGuItT/KkI=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538743079, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAATN9filqyLp5g9XIJpl3DxoP4ZGraolOjtp/cdFFVLuOkChjTArrCoZqNawle3yTcREJd8NSP7CNUDbwGSkv75RTaCghb+eyrtsGisUNvq5qO9+gC0xkxjUBwKw19rvNE8Flg+A8kxakNX/2z/hrV7WYUJVwG8w4qVKqHwQrFb8wOYd8jxYUIy5y17kspRFtXwj9l+SsOFnJ4JrwQRb090xvvnX1ZaqcmsznOpcyS5I+ZNyjcEDRYTrVWxn8ogjLZjBS389JR4QzuLZfskOfdvBEotsf0pAZ6tyvobgv2j4l/V7wqIoNNFpJJyOgDqZ7XfvC8f+ysnHIXKwdFNfRwIPhp68mWQs5AbQHCA0uQw7CwmNPLu9zuWS/k4IPmw0w6XU6JF/vd4vR/mW2mxiF9O7EYGPxozFVsVRYaA3FkfdjO9YUsEFtQX8cgXVt9YZI04W0GOijnIHAmKSTEhF6A0V0Ac5WY6Lu0+1Zi/rwe6VzC8Cll5JcMLiOn11Yp96+hW7qblCsudQTqFtCoksGjwM45qr/ocXGWgj3EULm4WtaGb29eCrIkLL8FCsJ2a5qX5xOEmMnCa1Xpl0toZHQolLn6Xtio3xqIPy4NEB8QOmZhc70mn/Y1rUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwiW/fMXUvHCO+tq9O6wnCS6d5tTUb4Qc6A9a+yRL0JNvW86uum2qpC0uumbMBBYXidl+cRNKaLHyacJzazFprCA==" - } - ] - } - ], - "Wallet connectBlock should update balance hash and sequence for each asset in each block": [ - { - "value": { - "version": 4, - "id": "91ff6e55-1ce3-45e6-986b-2a49f2fbc040", - "name": "a", - "spendingKey": "ef064203b33bed503b7ead158644ebbb9f4be9d997196823c4e12dbc28ee40d1", - "viewKey": "36c3f9ea6678efc017148e12ae4790dbea760d2a684ad4c564bafb36962469cd33cc899fc64b80e985bae058e09006fd38711fc31c3c988e109c10c5424e0536", - "incomingViewKey": "51b77328b548d6476845925043b2b68979db966a80045974ab1faff3eabc6b04", - "outgoingViewKey": "82631567836d529fed8d550459aeb2d0fd89b09a6b197da028b3e730edff61d1", - "publicAddress": "0c0e73741390118a4965ef6590ff7f76e88355b6c514619e6334733c19ca1bc3", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "846fc1e615e263752c522a1847bc2300a7c83bffd013d792d8bdea4b6c354e02" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:kqsHopOnj8uetmgQSSpqWl7NwU2AGGVQhwE+LT3bHC4=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:v30AX7dy356AAhKJ5ZNKRzrToMjb8jmWDZD4OoLulo4=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538749069, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAiocrxO+A0IiTliX0oV9UQcwHLKLYpdD7cJ0j2V8cdu+pnbIjDrqLdRgHN5NMOd1WLm8aG7Acv2IYX+WFCNuNC/vhnaRs5ogA5eOkkj4KRruPQtbiqoiBrwZhb28f27a+nCDCCSZBNVatKjnwYgfLLMj/HVnmGpMkIZv9a8o/BioA9cKQJjlRIq4RNreEetCSe1pCJJFlGGELr5HX7dEfopLhQU5x+afaOeq9A5etQd2EKShXQUlFmkk2+cLR40tIQQ2Op/yjRSIITmkWElVA/ce/x5i77DP6yp5k0e0lsEoHjE6ApI8dKS1A4L5+HFMNmfM6qOeSn2qSnSM9VoBGYijYDHxtNOKLJjkaCOYT8ttYwGN8wLQdm15w1C0MhWAEEi6INZq/8bUi65WvnLij6a/D62OEugmwLwXOC0EZ3cg/HIyl06L/ApuvYAzNnfCJ9Nwi1e8lf/mD2sx22X3N/QrzJ5Ff+pUTpUpb+uOnMhSLTZHq+pdWHTkRFt0sViHPCZVc+QOGfhbFhBX8HEntLEkrk0fn4Istg03/i6fwNnFRkXqmu1HBCzfplBdVCsmB8t4dZUiwW8wXSaIc9Syi3UoaMmQb1WMVfy50Ub8ci7usL3C8AJCszElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwShYcru/Bp+PpurbV4diRas83fjyjaiCN972Mm1BfxAzb6EDYy9T1Kv7qidI1hKYfqMHKJ5ga1/tyBOadOt77DA==" - } - ] - }, - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmnpeUzQ9D4Qb93JM92qkiCn9l3rluRUHW+juo0JZ2NeQ3UNTNjaHpGKVuB1l7fJuNkO1I6LAbAPXohfQqu9RonksgRP3hsqUoMsOTAiQtU6T+UHDrSfXpmB6puUasi0/assXd6nPOwXoUAca2hBsmEPOdZzVfl69Ln8qx9RZDWUMZLfumFNYZiKE3SDENAH44SzcdTFuBQ+aKG/xebUNvbevPfGMlZj+S9yPUx7ydvWgYr3BXIxG01i3X2XKdZpmZBPB03WvFfpe9giixiIRqdCM0qqmJHGCUXXvYlhMqklDkP1nC4wZE6oTznUiubQ1AJUt4tyHxWTOAOaZymJEotHcDarpMSsWhUAMQIHvtQPIL1O//8HwUc9uPjSDUvQxJuwQlvJ4Eu828VyV4QGieitDLs9QANVNxlatupM2SkQIThRGPfwKuTKS7MOFxRSypaLfJ5ZcrLVXao1topQTN4LTpiWLIsHKZVDzAVGUzlhYhBSf42/M+6B5m1xVD+rbjesbslzGYAqJDkkubuGq9B+fFM51VsZL3yuXCqH67IaQ20wcE9YpBWg3HDIQcXtprWKORJ2z863b0msW45yPfg+3i5mgM6GXxPiUkHX2FLF64Si03JE/f3iR4xJNl8dIL0RNl+6uOETHlxF5N+2cjqMjmuGaDlSABoK/MUKxb4rYr76ieK43LqirPB5+QP+QoEKaD71zcgH/+qe+JJq7oPb40Nxo8oKOrSN07fAFFKhE2m1XJNzyUwJEt+xrrokklCee55uymo2jsavy9ZBDVdZbeZ84PNUSsVdFLTGfXczCpVT8ZkFC16SmYjtMmtzcscwyOFp195G0/4AT2iu2K9/PPjvhNNG5ElzSUnfBL8Kv0CUuNfGaFgjM+JhBra5sCbdwVj43wip7rETwNpcxkO3gDNK5Lh7vuFKDi01uQmxJfZ2LAiKZpO3ZaT7JlgbuN4n36zQzheA4c/JYBHhYAeXoqWQOCMzhDA5zdBOQEYpJZe9lkP9/duiDVbbFFGGeYzRzPBnKG8NmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKAAAAAAAAAENTfUHSs0oLIBLGCs8WT8AV5UUGKbuGC1+GMHbOT7KSAwe1ogeoI0RhsIsT4xTKfW6OKGZs4bsBabdHtcVL6wnzvKeMQOTpIxXZpT2MsszJJDiie1hFKsN1cPvRCsxmRYJteu+w22+DLETWfxHAvWg1qjUslhpSnoNpd8Q3dfoE" - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "08E60EA96751B6C263CB3EDE91557A5A522F4DAB0D6C5BAC27381076686A134D", - "noteCommitment": { - "type": "Buffer", - "data": "base64:WR4JLjZ2xt2Ol8DHhGLXMuMzfxucA8TqN93MDlofpF4=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:SjDmHRvql99ZZ4ZQwyetDTfIxxWEqNehAnuOHYJv4RY=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538753111, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 6, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAxN0S+vesKhTrnUKkw5yBuZtFaPwSQ79HlGuEcpcKBrq3Sx4FOedDWBV+xzWO4cOi/BePz9o50Jw7Xw9scSpxhIVPXAfan86ApZvfEe4sY9ul43jAvStK8/7YXWa5N9shmdPxUai0rns2IZW35C8jFzabCHBiQkYJTszXtxJl308L2s7m35qmjgYGyGKbUhxoFeC9kPDeL7hLgK9Kx0U801ALpnseFPCMUoYWqjmg8iiO1m+dSdDsteothPM88en15C/C/wPTTCjBcIoy6r8oXqKEC9R/b8bUL/0nLUf9ZFMqNMOCuw7TQTOg13IjwOf9hFWd7mSmT24wInNKlKm+7rHh/oDYlMsemdyfTwBa0hvdFM0GMEPDsMUe548SRUdVoIIhkvO3FJyXd1RScokm6CW8U6mGKnyZX4GWXrLAqNe/Gvw8kBQRi/v33a5IN8ygR9xv2XshNGMW1OBAcUQxBeSjP+LCXLlqOUIXSDY9ySxF92cz15Uwjru0OPzGMmbJrhASy3kiQXk42acrZ2A4Iid9tLYreOGbImSW7ZDzhCcZwCDarc5PYpSGB59RuhbYoENnPHqyGa1ICLoyu9W3yD2pEvNTpkeaHRLE7TOg1Rlt7XVj5u2RMElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwn0pwjy7MY850X+mWqQHdF8hfP/NJc9JZD5O3DYZMlTPyVIlxKM2Uk6FvARR55xSl0l4OlvEQFVRwRq1kd4zfBg==" - }, - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmnpeUzQ9D4Qb93JM92qkiCn9l3rluRUHW+juo0JZ2NeQ3UNTNjaHpGKVuB1l7fJuNkO1I6LAbAPXohfQqu9RonksgRP3hsqUoMsOTAiQtU6T+UHDrSfXpmB6puUasi0/assXd6nPOwXoUAca2hBsmEPOdZzVfl69Ln8qx9RZDWUMZLfumFNYZiKE3SDENAH44SzcdTFuBQ+aKG/xebUNvbevPfGMlZj+S9yPUx7ydvWgYr3BXIxG01i3X2XKdZpmZBPB03WvFfpe9giixiIRqdCM0qqmJHGCUXXvYlhMqklDkP1nC4wZE6oTznUiubQ1AJUt4tyHxWTOAOaZymJEotHcDarpMSsWhUAMQIHvtQPIL1O//8HwUc9uPjSDUvQxJuwQlvJ4Eu828VyV4QGieitDLs9QANVNxlatupM2SkQIThRGPfwKuTKS7MOFxRSypaLfJ5ZcrLVXao1topQTN4LTpiWLIsHKZVDzAVGUzlhYhBSf42/M+6B5m1xVD+rbjesbslzGYAqJDkkubuGq9B+fFM51VsZL3yuXCqH67IaQ20wcE9YpBWg3HDIQcXtprWKORJ2z863b0msW45yPfg+3i5mgM6GXxPiUkHX2FLF64Si03JE/f3iR4xJNl8dIL0RNl+6uOETHlxF5N+2cjqMjmuGaDlSABoK/MUKxb4rYr76ieK43LqirPB5+QP+QoEKaD71zcgH/+qe+JJq7oPb40Nxo8oKOrSN07fAFFKhE2m1XJNzyUwJEt+xrrokklCee55uymo2jsavy9ZBDVdZbeZ84PNUSsVdFLTGfXczCpVT8ZkFC16SmYjtMmtzcscwyOFp195G0/4AT2iu2K9/PPjvhNNG5ElzSUnfBL8Kv0CUuNfGaFgjM+JhBra5sCbdwVj43wip7rETwNpcxkO3gDNK5Lh7vuFKDi01uQmxJfZ2LAiKZpO3ZaT7JlgbuN4n36zQzheA4c/JYBHhYAeXoqWQOCMzhDA5zdBOQEYpJZe9lkP9/duiDVbbFFGGeYzRzPBnKG8NmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKAAAAAAAAAENTfUHSs0oLIBLGCs8WT8AV5UUGKbuGC1+GMHbOT7KSAwe1ogeoI0RhsIsT4xTKfW6OKGZs4bsBabdHtcVL6wnzvKeMQOTpIxXZpT2MsszJJDiie1hFKsN1cPvRCsxmRYJteu+w22+DLETWfxHAvWg1qjUslhpSnoNpd8Q3dfoE" - } - ] - }, - { - "header": { - "sequence": 4, - "previousBlockHash": "2B9E5B414C7E314AAE120E9F60A334F7056B18B2D4FEF4137F64D0A5AC5E252B", - "noteCommitment": { - "type": "Buffer", - "data": "base64:WsrCaDVLDY2un9xJ8QN49kaSqPBFU++DXpSooG9lnU8=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:XvzBdub5bJaan40A/CIfy59FeALOAwFbz76dRrlOMAw=" - }, - "target": "9228823284279306817296266184515742822248210830185427859262273659833347", - "randomness": "0", - "timestamp": 1717538754915, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 7, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAC2aq80g2qa6cp5aZ55Mqdjrj7mLP1F8VzVsJLmAODJWt5mz5Oit0CHDAtnG+xkEOrI9R6SbSE8s9oAeR07XimlMvjaQWUGllv6SfuzUQ2uSDbDZkFf+AqcIG71ku4Whp+lOmTqS0xlBoKCSSM5qnuEwuKFd5T+yXDkRKSuv6Ad8CgOM7s8tqm4skjb64EX/pLc6BbnaNUfkRUMQOWSEPxHFeT8MMtHnW5BBV4bYi9VyhkQTi80GWRS3547uI9iVnIKJBil0rEzKW/UpwsefxRtGUhkuuvilhM5hohdiWzu8OO3aSiXbkNRdJQa0Bf1w7jlXE2h3TAJa2tWLGVtWq1cseSezAAB1SHOnkWrDTC5nLWYJR6LSGw+b8KcOasMJswKMimw3KtTWwEQbgNLARZSAe6ZZEntbRfTq3Cuh/5BOvE+IixruzDz6zmAdTL130eQEDobiEi63NLiCcIkvrktYLUKsugJKxYQvtF4CjyVKoU06uitmBjwH3D8uS9WzVGATqEDfjBPDHuA/cE8be/Ue7xz6djoBBsGec9vHn2Q9yF5qXwmh4iDYdRrJlAbk4wlFjqt52D44irz3Xm+omEnb/8KZItcgIkDPYd+TFUVsIRz3/YTjrsklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwWbJzidWm4ZMmT3fo0cM8iGsjUl/GSUQFZSqFwv8Qo4JrEygtm8WPEaUnz1w83wikE2n9JGZedCZROQhUU5QxCw==" - } - ] - } - ], - "Wallet connectBlock should save assets from the chain the account does not own": [ - { - "value": { - "version": 4, - "id": "915b4fcc-0b83-4003-abc3-b3ac8d2b3eb6", - "name": "a", - "spendingKey": "8d8da90ec36069f7ec0c039f02d5b175b15cca476f4247a3e3cfeee4a9120cbe", - "viewKey": "a871dada21ed96d1afd20e52752e46aea17bceeb0a86cb52da60f91ff410670e93851c697cb989036d95086b01a4d2e3cd799929e3e0594c6bd0d65bc937c605", - "incomingViewKey": "1ba43b83564d50c2d89c0380d692cb0c9605d8dd28622c9b9dfac26efaa4fa02", - "outgoingViewKey": "61893a6f08648028a4bff39a7586a70082bab7dcabe6fb0597068dd1c2089af3", - "publicAddress": "9d042d3a4438807b1225f6e9883c6af634460304362b1e1d1d11a985b47f0823", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "7b5f73a54f417ff870e556af7c73696488dc3c7515256ad261bcb7389d73520e" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, - { - "value": { - "version": 4, - "id": "2bae3447-51c0-4701-9b4f-7fa8ea787216", - "name": "b", - "spendingKey": "cb8cf2b9ff149f3d4dc4ff7cb60493ea5c77c5fd095c169aa6dbea0a58a241bf", - "viewKey": "2fce17e766907f9b8baf3654be6578c0f8972156e4f3f0ecf22351df54f7fa9b009ba948524e55030ab53f269fa58ca21f5b673d8583a432e88e8556e01268da", - "incomingViewKey": "3cc5ae32de87f1f557533e5a7c6c92fea4822b30b314e0c4e7ea4fd4488ac802", - "outgoingViewKey": "2170357e32352fcc6eafd2fb4b0659dba71d142a6038b157925f1ebf66577582", - "publicAddress": "ce2bb4c55cbec067fc8ba9d94f780b3def2ba2cef6c0a612c8d88ca3741c4c85", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "9c3e2cbf70b994d5b3dd251a21630a7e03be438afc52a076361c9c7563c1e508" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:+FjNmRVawduRQiIigLK1xtwcf1a9CzZfmlf8Y/1Sa2E=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:G9DvNe65URf7a4GSV5NBWdgmRxK/6ROQDLJiErMD4yE=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538757782, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAABeV3VFs52DJ0tY4f1l8opWZ1VM8zlob1V2AJLaLzdRSES8ckskGQinDg0u+gLpy76K7FNTN1K669jCazuq6D4qXnpAUrahVNcpX/kw6ZUIC337uyFvN3YRaTRsAGTolWkvUlpH/xCBpDK/xCfYOkK21VyzYzmXir7hmHDzaHJZABr4Y5/F3GRt6OHP+N2Io24A2O9MXIftZujrzEtM8dK63ftB28M9J6tkHXILIDiweIu12uYwQwHZhq5xXdeW5+uf/vzYLlF7Cg4gllFD578Kgo8m0CcnzPxzKdRhvuAXFhbeo8hSPqA7Tn/tsUlGCVzF3b/Qs7H82SSPN6byjeuzpVZN7DE9mgXOHPCg/dyDWlph0mKTTglBzXwzGLohI7xNASUv1w/AR+q013pJii1bsN1mXoFrhz80Pg+MW4wK06CVIkThpjxgLP9jgswDBxW35XTroUWsxfeC7eiRuOkEF0N+OqGBO0DtL3cMyphUdjGOGIyPZgJ6XouwEExO1qfuThd49e2y7N3rn6k/89MfX9in7l3QdGhPM9XNhbL7kojUlEaTLtphdGr9NJ3l5Yi8m09YbOytJrHVIIt9MCoYbLMQWs2i4E4HKe/TtGFmkT3CZY23nYKUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAww1HQ2VV0XSjCszDQaDH12BdxxjbHz7AfRBt74JPXRes4UK1lVO1stYLA0P4RRIpfYhOjj/O6AB9IYQ6EsZZoBQ==" - } - ] - }, - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3cYsnSm2iYH1hnUiD7hsi+7qbQlyaJFE5X/2zSk5QqOIXnh0YsRzzfOFj6m8rZ5e6DXEjDb6UhzWSy53Gi5CFPjA5NIM8SSINFRMe99OzxeR3stc3bUXrzvarT6nMlUecLBjAMgIHGoWjqROXGLCepMQykWodbyWcY7U1dfXXfQXjfH7yBM9ggeho9fe5gHzmXlgfwnZVZxiM5lAYaljv+mM55tJdM7qzusY3JoU2QWPwwj1GEhdYpgT1IY92vp8yo5MP5fMxMY14fqI4zSoUlKclCcyrREiA2v9iJoLyvbM1RX84lb9XhbswJKwYPmepMYZlMs9vgvWJD/e1flmS71ktW3wXawnJKd+u/BivI4q7reCBhYI/wPpGOJRzI4gRIKT/H78BMjyqPKpoDMD1zvLOvoyKJ0jaTktbe76P+NJOA8MeB7PRXS4JPG2HdxqPbzF17GX5OaC+brazhjSlOq03OdRsPU7lIGGEPvkv6ODhCr1fNB1bqW3I1OT98u0Hcyii1apTA35n+54FND8FwIitKZqsKlWMNFlvQr28e68RG2+loAVpeDwdDKc4wSuZJ3tK5op1zxvlBny37Yv4DuwgkFoCBXlFMIa0BboBFripEx/mJ3hZV5/hLiVIgsg3dpkbLUIQXnmQPs2t9bD6he0Io9OuVTTprxKgz1GKh6e8PbtRZU0F69ooORb7X9PasXEitA1MbQV0vXm5kZdzShwh8+NixMcmc5I+gp4Hvj9hQlFkwnYpXrGyTVQNSfdKd/UxZHOK04Couo7G6iUKcUy480r/JifjTJbodotn9e+yTMarnt25i1K2xz4KXMnxxuJwpFTl2BD7Nt9/+quCx6144XsPkvnDpAkg/SBwtjBDa+0K/P8JyDU9nIZ3Ajh+tyEtI7Z8GtXdLl3M0cuyAifI5T3edDCkg12x/AwpGskL5W/gWoAKCvpKr1/uE5fCJpBNODuBVajrCApD6cEyByhELXbiRbenQQtOkQ4gHsSJfbpiDxq9jRGAwQ2Kx4dHRGphbR/CCNmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEKAAAAAAAAAEJ97Ic0jZXLSfwvPOD1i3qjdFAtexdxqms30odGo/lCPzwYZqco9QuayCmfeUYitQBcmx/TGhzJjRoL+JxdDgAGsyBnEpxTaQHSaxaT5dSJ5OwadNp7m3mPHcqFyQGIp155DeXcil0EpssKY0sg3M78Rr+tgbINqkuERA3Nw30L" - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "CC0B49C422C9E3044EF1A99DE6E31BADF542F6C7CF7582440C83487E33A24E25", - "noteCommitment": { - "type": "Buffer", - "data": "base64:45AjYssDI98rufnehnLNvEqTEDRyXVQuo/AgZ3YdIDA=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:muBBl+5id6qsKh2H35/dXNCY8LmlB56ioE1KHFJw6UM=" - }, - "target": "9260366780148527510972123832573278885902566341756516011968728852484845", - "randomness": "0", - "timestamp": 1717538763488, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 6, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAECBGMoUQuumHxBwbg6at0a4IWqJiFWHnLFQDKpC0OiSwVSczR1LhnKCJrHY4BS0qZvLP11LkWp2HrGr2TLal98idMdbrHsE8QMrnFLsjnXm57tOqktkpVZ7/zOsnqBi0xjsucFV3q9227+ynFBNU6XhS/ooh5zfBxPw61XEVQ3wHvJMbqRcTkkTzPEUPycpKYFTfwPmTUpLe83QhGOyHzCU2eb8GIQSmZj+ectegooaL1uyfmxEgm8B2i3a84n55AXn40S5UFdk/c5foAhYNus1GSAyWUIVSxJjK/HMSR8k5M1GaYTZwMyPj20TpKHXxOYjl0IJGHN1vKiKS64FXa43PihmoTA6q+5iAj4P/C3xWPcg/M7nQrsDCn/oFFHE0EHc+I5JMMVjf7mTqgdKWhNtv/EQm9u84KotEQ8Hdy4r7Bx5CtnwroSd85uExgoLF+BQ5vd0CBtRJyPJPy55LGs0OnFnadblffQ6UF6dyoFnJfiB8/eMMjIklqXHQJ9HOi9XHj5JXK+eGfd5GCg/bQBKNej/dop/ANHFPFphoVA4VqlgNyzjWEtR6vmyhP1q38ejAD822XegqoObHd5bf4cHyGMd6ZQvdko4gdUaTqc4GLoZv+ByNW0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwf4yI/wEIEr0qB0smpSnB/pKPPCdgG04kcHbsCGqPBJerkXjeL6WXtqFZsXgYrzwGGHhg5jmcO0ZghgrStfC1Ag==" - }, - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3cYsnSm2iYH1hnUiD7hsi+7qbQlyaJFE5X/2zSk5QqOIXnh0YsRzzfOFj6m8rZ5e6DXEjDb6UhzWSy53Gi5CFPjA5NIM8SSINFRMe99OzxeR3stc3bUXrzvarT6nMlUecLBjAMgIHGoWjqROXGLCepMQykWodbyWcY7U1dfXXfQXjfH7yBM9ggeho9fe5gHzmXlgfwnZVZxiM5lAYaljv+mM55tJdM7qzusY3JoU2QWPwwj1GEhdYpgT1IY92vp8yo5MP5fMxMY14fqI4zSoUlKclCcyrREiA2v9iJoLyvbM1RX84lb9XhbswJKwYPmepMYZlMs9vgvWJD/e1flmS71ktW3wXawnJKd+u/BivI4q7reCBhYI/wPpGOJRzI4gRIKT/H78BMjyqPKpoDMD1zvLOvoyKJ0jaTktbe76P+NJOA8MeB7PRXS4JPG2HdxqPbzF17GX5OaC+brazhjSlOq03OdRsPU7lIGGEPvkv6ODhCr1fNB1bqW3I1OT98u0Hcyii1apTA35n+54FND8FwIitKZqsKlWMNFlvQr28e68RG2+loAVpeDwdDKc4wSuZJ3tK5op1zxvlBny37Yv4DuwgkFoCBXlFMIa0BboBFripEx/mJ3hZV5/hLiVIgsg3dpkbLUIQXnmQPs2t9bD6he0Io9OuVTTprxKgz1GKh6e8PbtRZU0F69ooORb7X9PasXEitA1MbQV0vXm5kZdzShwh8+NixMcmc5I+gp4Hvj9hQlFkwnYpXrGyTVQNSfdKd/UxZHOK04Couo7G6iUKcUy480r/JifjTJbodotn9e+yTMarnt25i1K2xz4KXMnxxuJwpFTl2BD7Nt9/+quCx6144XsPkvnDpAkg/SBwtjBDa+0K/P8JyDU9nIZ3Ajh+tyEtI7Z8GtXdLl3M0cuyAifI5T3edDCkg12x/AwpGskL5W/gWoAKCvpKr1/uE5fCJpBNODuBVajrCApD6cEyByhELXbiRbenQQtOkQ4gHsSJfbpiDxq9jRGAwQ2Kx4dHRGphbR/CCNmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEKAAAAAAAAAEJ97Ic0jZXLSfwvPOD1i3qjdFAtexdxqms30odGo/lCPzwYZqco9QuayCmfeUYitQBcmx/TGhzJjRoL+JxdDgAGsyBnEpxTaQHSaxaT5dSJ5OwadNp7m3mPHcqFyQGIp155DeXcil0EpssKY0sg3M78Rr+tgbINqkuERA3Nw30L" - } - ] - }, - { - "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAedLOklrBrXa6G/YBss5RRGHYAagMULHeqHtvM5xKrcyKoPph5eUMx51wYSH6UVqR/LmnHb93wCq8sdGE/3jManN0c+QbWoqcUWkoQdEFePCqybcpaKPjIPZlpoFp+iF1XCvmpxuZmFPjjhm0SRY+542gF85cp/agktDzn/yYGxAK6UV++wbBD8L2e5HViAtp6PgKRyhIN6RIvnBetatKFSCU/jGF1kTPOq90P/FAjQyTmdIzINRtAVJo6H9dm70F505fz8S2xzVWAvUu96/DT0MvUgLqA3IGQibWhNoMTSVHrQoSoxa8RoeZnsi4gsdN760TelMZWF+RrtTFPCfdh+OQI2LLAyPfK7n53oZyzbxKkxA0cl1ULqPwIGd2HSAwBgAAAEg2kYlX/ZNXTP0u+iLXVPS+2O/wVJTnCi0ynjwzJBGnkAq1IiVp2iLlOPJCo49wYHN8nto33NsOw1p+wwzY85pf0cQAWblkYceajHoNH0r+XTs6UQFSpD0GYI8GyTaIC68i1AHqpJDrPDRcn8gEpdPjVsW6zZcHQHRYWuv7cr182T49zYI/VC5+0de+iAuHZ4O0Q7lJ8eoId7Wu45O1qhxSe8Q9PGZZi6X4AqXRAQNb645/gLCaWq0ctCLOA2DcnQDfuIUZJP+gDM5GP0AUKfxy5ixtcLQs47GRyhBh0LULNOxtE3u2YXBiJ48ec1E4oq8jpNnX0u9AuQYaiEjOpcc+mPb9XsWzZ+5azJj/esJIrdMhCuTRoaMEPBPmQolbx1H2nNrv2DGbjDJBSmJz8UhKopiiUxVNY1UXqtOMgkWkF+Wknb9bDjQDzIX01xMm4J4eepOWjC14b5X6zAUjvyKYtr4h5m8+yWKMyNRt6mkMYdMTbAkBxIpODgltGP8JLN062AneB5YSWCfhzMdFCccx1a9HGHs7t2F1SeV4pYWBfAi4d/9NghUg94AWuqQrInqSf/TBi4FhK8x2eFwt3JThDcnAjRe4SG0Ny5kwxh+gI22Chc7V6IJuIROUfUQooYW6ZX7JlvzaWrbXWg06MW00I771JrOCn7m2P3PgSnRgmWw3bivugFzpKul1WxQd+TD6pvM22fWZ2BW7Q9qR+B+u5UqEf7cka/xfBiGJKKcGxO+mpsXRsfCeOBKt/8b0VUcrlIxsW9rDu8RRSMxyySVu2Kl50I2HPIW6/IfhZTvJqtxX27K34FGKc1vZRnthIqsAz1Jb3CPRnAUob66rUG+a4A6DpzjbJEZytisee7T7GMJgS3pkbFW0PRngpwa1JlT2oTeVeIo4Ca55ETq5OfQOw+y0ot7qGImKI4piQpspEp326oAFDDUKMhmjoD0SHJBmElcxulL8eanyY55ETuQDbh2HLNZX8neX626gk+WDwmZdYHumJXmMhsktULqdPzdtxOySq9fNd9T3rk1TjOtt0xNNMcJcPXURob7Egfj7NUqyPbl4oENOvEvMMFxFEJiiF01seT9nfGdiGw/veypRxR9/Whhi2fWcr623r2OuesvZaE2hmuxn8taSK/Zap5+NFo0wUncQawtSDjs3NsEl/AjPZcpuebAv8ERg09AGKstVhzkv4dY54jh4CmvzINdRbW44nRKASLA3wPeUFdttULbYyx3Nd0AGQaI+SizXW5F6oC6+dlv5x3Z/0ebiIpM6sqB/xgj3ngpas4JMzBU4Sg21RxHEdTmkgpwSAuwWDpaKXHWz7k9WZf3kxpcWaeYM9EVE7/i++f4ovASpO7oJu0cW2cJLXv8TaLPF54O6LeYTpLCPdWMU39XFJvtfQq5S6jSwW0iop6aqU5KU8BSsHoC+xWp/fHyw6ZtE6i8O1MBgQPixd544qZgP2VkxMNkuG9gRHSNWmTi81+D6Mp+hEXd2zI+vMco6Suz/L/OOdg/VhuRw9HgQTs+K/uGaafP1n2LdxB/N1yM1a9hZHBEMKMbgFOkLGz3wRy6g6KIw7cOFgMr89Bk/22f3nYKLAw==" - }, - { - "header": { - "sequence": 4, - "previousBlockHash": "E76F7B810F96BC5B5285D71ED22892BB7C6F01E5105E7EED220B185CFE8DBD13", - "noteCommitment": { - "type": "Buffer", - "data": "base64:A11I7xz+8WUxjQjMmcQrBIFT9qdoltoulQ03aeIxzBA=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:N4Ld7N8M+ia5EWn5aE8HKCiYw6ZoKAFxgzgy/lEQ7wA=" - }, - "target": "9237815341750015092140817300043113376661752366206318446334046747329935", - "randomness": "0", - "timestamp": 1717538773587, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 9, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA05iQ60xabSSeTHFqbr84VYSNtfkbOd8+2i6CES3R+qO03Rf5BQg61ZNqnjjlReA0rV2LTBLVtldjXjd6cUPvzu5m3qKYc9u6/i+lhv2hmluw57qhpGEHEYLH2vL/tI8xlodYWrAemdWMgfJZ1TB4KjLyUlZjjymYBROlv8jNh+EGSdBlTa7m0ZS260gPui9aeQhQV6bFluPSU/4cfHVHQjFUm02pkSSGG/DfMlHoQSyAIO26NYqegK1CtVfD+iItQgZnV+5bD7J/Lk/U/bdxwuX3g0St7hqD3SRYfy03nZ3xu7LZLkCROer+UOZUrVR+tyh4e0vGZurmTuG3twY98Qv7AwqUn58jBkx7MyvjLQHOmCdqoSA8g0eR+zA6ffpGuIVmAouqfgVP8ARz7HJsOp+VbjB9famOZ9Mdze1qqWkWDn/tTAX8S87gMCpnr43bQGZCMU91bqS7fT01vZjSGgbpnJM75h9Pl8ayDp5nfwLjV6VMeNqo3dcY2GK946Thd//iqA8dNo7x/xTJogmG8nyrhRbO0pEvAZcxopuaBMmEye44Le7yCZiAuC+ApSvc9mplNs782iQUaFdZ/Suv3zJcbd/9gvlK8UwYtzkFEZ4XNAX0uy/i+klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwqFeeYcaswJOX2ikmZG8s58NffYbgk4a45kDbXB6A3HD/WfL0TZRrq1uzb9sccPS79+Gup+2W9g+aw/E00/j1Ag==" - }, - { - "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAedLOklrBrXa6G/YBss5RRGHYAagMULHeqHtvM5xKrcyKoPph5eUMx51wYSH6UVqR/LmnHb93wCq8sdGE/3jManN0c+QbWoqcUWkoQdEFePCqybcpaKPjIPZlpoFp+iF1XCvmpxuZmFPjjhm0SRY+542gF85cp/agktDzn/yYGxAK6UV++wbBD8L2e5HViAtp6PgKRyhIN6RIvnBetatKFSCU/jGF1kTPOq90P/FAjQyTmdIzINRtAVJo6H9dm70F505fz8S2xzVWAvUu96/DT0MvUgLqA3IGQibWhNoMTSVHrQoSoxa8RoeZnsi4gsdN760TelMZWF+RrtTFPCfdh+OQI2LLAyPfK7n53oZyzbxKkxA0cl1ULqPwIGd2HSAwBgAAAEg2kYlX/ZNXTP0u+iLXVPS+2O/wVJTnCi0ynjwzJBGnkAq1IiVp2iLlOPJCo49wYHN8nto33NsOw1p+wwzY85pf0cQAWblkYceajHoNH0r+XTs6UQFSpD0GYI8GyTaIC68i1AHqpJDrPDRcn8gEpdPjVsW6zZcHQHRYWuv7cr182T49zYI/VC5+0de+iAuHZ4O0Q7lJ8eoId7Wu45O1qhxSe8Q9PGZZi6X4AqXRAQNb645/gLCaWq0ctCLOA2DcnQDfuIUZJP+gDM5GP0AUKfxy5ixtcLQs47GRyhBh0LULNOxtE3u2YXBiJ48ec1E4oq8jpNnX0u9AuQYaiEjOpcc+mPb9XsWzZ+5azJj/esJIrdMhCuTRoaMEPBPmQolbx1H2nNrv2DGbjDJBSmJz8UhKopiiUxVNY1UXqtOMgkWkF+Wknb9bDjQDzIX01xMm4J4eepOWjC14b5X6zAUjvyKYtr4h5m8+yWKMyNRt6mkMYdMTbAkBxIpODgltGP8JLN062AneB5YSWCfhzMdFCccx1a9HGHs7t2F1SeV4pYWBfAi4d/9NghUg94AWuqQrInqSf/TBi4FhK8x2eFwt3JThDcnAjRe4SG0Ny5kwxh+gI22Chc7V6IJuIROUfUQooYW6ZX7JlvzaWrbXWg06MW00I771JrOCn7m2P3PgSnRgmWw3bivugFzpKul1WxQd+TD6pvM22fWZ2BW7Q9qR+B+u5UqEf7cka/xfBiGJKKcGxO+mpsXRsfCeOBKt/8b0VUcrlIxsW9rDu8RRSMxyySVu2Kl50I2HPIW6/IfhZTvJqtxX27K34FGKc1vZRnthIqsAz1Jb3CPRnAUob66rUG+a4A6DpzjbJEZytisee7T7GMJgS3pkbFW0PRngpwa1JlT2oTeVeIo4Ca55ETq5OfQOw+y0ot7qGImKI4piQpspEp326oAFDDUKMhmjoD0SHJBmElcxulL8eanyY55ETuQDbh2HLNZX8neX626gk+WDwmZdYHumJXmMhsktULqdPzdtxOySq9fNd9T3rk1TjOtt0xNNMcJcPXURob7Egfj7NUqyPbl4oENOvEvMMFxFEJiiF01seT9nfGdiGw/veypRxR9/Whhi2fWcr623r2OuesvZaE2hmuxn8taSK/Zap5+NFo0wUncQawtSDjs3NsEl/AjPZcpuebAv8ERg09AGKstVhzkv4dY54jh4CmvzINdRbW44nRKASLA3wPeUFdttULbYyx3Nd0AGQaI+SizXW5F6oC6+dlv5x3Z/0ebiIpM6sqB/xgj3ngpas4JMzBU4Sg21RxHEdTmkgpwSAuwWDpaKXHWz7k9WZf3kxpcWaeYM9EVE7/i++f4ovASpO7oJu0cW2cJLXv8TaLPF54O6LeYTpLCPdWMU39XFJvtfQq5S6jSwW0iop6aqU5KU8BSsHoC+xWp/fHyw6ZtE6i8O1MBgQPixd544qZgP2VkxMNkuG9gRHSNWmTi81+D6Mp+hEXd2zI+vMco6Suz/L/OOdg/VhuRw9HgQTs+K/uGaafP1n2LdxB/N1yM1a9hZHBEMKMbgFOkLGz3wRy6g6KIw7cOFgMr89Bk/22f3nYKLAw==" - } - ] - } - ], - "Wallet connectBlock should add transactions to accounts if the account spends, but does not receive notes": [ - { - "value": { - "version": 4, - "id": "4b46528d-35ae-49fb-b3ab-c7942ea92860", - "name": "a", - "spendingKey": "312b7d60ca3fa451e1c737373acf7ce5efe871e04f978d0082765bbdebd8817a", - "viewKey": "8ca1af5cc4ee5b015ecfbf8eb4bce558746ee8109bc8cbe73a5a62e9cfd56098fcb36934e25fb4b61d0b0300c0a31a9e08f47f3b57446e60f9a2f0289e24d8d8", - "incomingViewKey": "875532d7b4a7a26386ddf29eff49a72b6a25b268167b4858af04f10c30a9ac02", - "outgoingViewKey": "66408539769523de7eca2418517b62cb02be7c401f0a7501bd13640d080e9c3e", - "publicAddress": "460d857e7d44100aadb1635d84a2d792131ccf3b111a3142e3551ec4c2f2c239", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "ffb1665a2739ebc9ace5a30b71c5b20c5957c6e25b937450b187028e80833107" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, - { - "value": { - "version": 4, - "id": "f2ca9dc3-8791-4db3-b590-c1723c30292f", - "name": "b", - "spendingKey": "07cb3a95ad4fe711659a9e35412fd0ee823ed9143fa9657a240457142097597a", - "viewKey": "38ea4aad94993846593c6483eb17e99f5231732b1bc67eabdaa23c28c7dec6ce031d854afa303382b742cbab41f240b6ac03b473c4a6973d8c2332c938e7db13", - "incomingViewKey": "eadda34d84590dae853bbaf51d2c2f7e5b4d9e529d2a939d27d1ac32614f9501", - "outgoingViewKey": "4086253445b82ef43620ec4c013f76a684d7f60777b27e308dc9956369a79669", - "publicAddress": "3e2e7615c181f2afaa6423465e3b3a9ddf8d89020891cd5f1fa77627beac0d4f", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "ef3245c0bc7b577ad96755d1401d21318f5c2325a6c9ec635e2860e3b8447c05" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:2YEeOO7CW5anvMnCFj4+fP2zXO5y2uMz7uYPIbEqehE=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:nOkfpH8xRjxR0cYszKBVE+pynTfSn1/Im56lD3tmO+Y=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538776794, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA6gc0ZMKzVuYVGSGcf6ZStXbFhEhQGBdxZ6dp2MGcpBiqXW+1GeVcLuzGE0XCPoATF97RUQS93leoPPXEZuA9QxSmGG5O16C5N+NvCytJUQmUGF+f1CP1GPJYPApvqU7ZeX0gN36tFc0XmFZueRhkfaox6WXfY/nLn9sGeEZX0xEE6sBXYqru2NNST1fc9gpZn6pHTmG/DPAOF2h2eMyiy4qWAF1ZxFdry259sTpg5biUJdDHdfC46rgLggtXOG/HKqfDTgGu4seax9isDhriSqw7Wiy+RolXEN4vlsoHWTI/88nbrvNWwTMmIFNTJlnDPcQX9XbkDGtWmms61IKoBicAa0evNtu8e0lmNjnFR2pyugcjkqVshZiLwxwwL2Q1onxO+SOfIeX2bAO9svJfdisHU+bsUKya1thsnb28l8jWpPB1y2ClwGJoFznubXVfygKkwqNqJwDPnTSXoT51S3K0iCnuz7/7vkAWBc1XoMY66TKSWkrc3Y5eroOZEoxcZYgZ70I4yP6tpxRdk5aNE1269BFvGaIEoX8Wdmilyl2xdcCEDsRGCkduBs/+ndcMOrBHmHCYve6h1dgZQkSA80autapfNFaLKJuXy93FAKIq/afXZhStj0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw0nYJUIFk2FxsS5XGthmLUBbrOrEJATp70MNA9plgkBYka4DxZbDJkaqvGhhRsWlwePT751lKGeumD6oEDV3gAA==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "3D2FD56B9C13A353C03760F88498F76DE18F31F1906D694C5BD0350EABC70F49", - "noteCommitment": { - "type": "Buffer", - "data": "base64:2aPW4nN3mBkAjPUddXJioPN1Wzb7Ccl6GwO2JE4ECXI=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:XVIsnhJCqY4oxukCRb4wLhvgU3h/hp//zO8NKcwRxz0=" - }, - "target": "9260366780148527510972123832573278885902566341756516011968728852484845", - "randomness": "0", - "timestamp": 1717538785387, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 6, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdiUEf////8AAAAA06jmlLwr+QR1va25HZIgnlFKNygW0YKgd1/hsswEaL61qnTHDAhtcJ1dgkoHHzSstl5O+27l8B6JDhV6LGmFGtzvxFU5oX/yP8SQ5gpFtoeDx4fkS70VU4jOGpulhIC3dgNsajM+mygbMVL2rGxWODo/kS9UmaXXWjKcVlbartwWNuwgeSfPbK93szYgCsHjsopG2CPWDY7+8S1T/eiI9UiSOhmpVkJ4vz5giU9YP8OIPRbJYNBDyi42s1M4oE9yhBiYmwWB72Te13hbCKXNv9klCubAoOz0hEmpHpRdia3h8t2L2mV/Px5vq+BvRLjvBuDFPxh+tXPMhLLz/Vm+gYsTXHcakQOYQKEhS78DObBm52aWXIu6NchDoqGL59pNKDa4RLzZ3qjqZKD8u98Mf0uLP3PNHppWj6MJGUSQsEYZ81wE0x83Mw7pVBxzNYV6ZcKjSW5WMGlByPpvYMsk2GK0RK4R1/KZg5u9OQ9vsy/z6Yowkn1HLgIuWhtUEzR4N9RO8tgql6xupVhTlJwhQUcvAIwIebBev9vsz6vqAXK0Bg78pDINhPO/XTdyokiKJmqhzwblFBp+ggmCebBF/TSsd9DBnLCzO3/yk+tYpb27bq2MhnYyvUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw1eoG14RNMtO9fK/cPOw1Zb5kANYygfdFNkSauFMZ98we8JoGw9PLNdVzUz3QUO/fGgEazju9WEIxJwYQyVybBg==" - }, - { - "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/5M1dwAAAAAAAAAASZxlWuOu0Ps2ueBKEUuioZBPdkwkIwYZGlO5Dr5jY+OqdkRfBzzWOTej3VCj8BzOcc1jFTMeqxDJXSCgKn99TLoXpnJ6ZT7p3KMfUhuROKmA4azQ3l/1UYuH0EBA26BTu5/dYFmmveCMTqZqtOEbsqyHWrkzTbtNka1RY7z02SsZKkHCwNtS2H/Ea6kDwPyr8MmxfC+mqAt0vsoimtZc2/UG8eg1syAalj9aSVgVzoWmItbb0YhZCxSD/6osnyEo1CxTcI2UkdJNIRDV0GHmFDqd3J19m8i4Lj9oWS2MuDD5bMZN2zbfTPdrWZ4QRBU9MMQLNh+lIZAeUkp67SvrBtmBHjjuwluWp7zJwhY+Pnz9s1zuctrjM+7mDyGxKnoRBAAAAAohLf87WCUBH3JIzCIUSSf5IaT1QDG7kc5btsFcpNvmSp3h+62jTwLcX3WNCD2bVdry+ihDricLedL9aPhgIrYnwEcdZb5ULlr4w0RoaE/1S/2/mw+TJJ7Mrsk8l+8JBKUkn2RRjYdnAboJDK2HYpnTfK5fM8UMf2bbyZPGHbhHRaamIPAVpE0M0aHcf0EXlY+idaV3imHGj3sDWLmshbJzX0Dom5IL5XikGwSYS9r1gxgcJ/k6r041Ga+N/TcrqQvKj1fBzL7HI9JgV14Qk8V6x3N66L4lPxK49PL7wuPs+8fbmQmRvOuInjStaIEIUZijacDPLrvqP6ASx4M5G2MnQxs5wTsxXkDZT9B7isULVHzPT9gEYR5YDt9bx5E5sM5aUnr0Mjnm+HpxwQZqE9zjjkOKhg8NAUryUjGExwyYS1+py9WO4Zq4V5oN4JNj6z6TNN4EBIcnKUGp5P+LtADR3bN1DccBFFpMiFbVwfSaAiLJKJ3hNd03BpxjUb924lJg7XegbLq65zNuDhkD4x/asRbMPtV0Nr/wjLAAWiN8zAegPsDs7xf9e5KmbQRc2OPmRfs2xrA5/ZmRKMr2JhG46McwRlaz+fU5sQaQX9AQhsSj6cvQ84y7YRcy9pmpOyB6TRsu5NOLCe8pJyjmsJ4Gt8ydQCHMMhQjw2lEUDzvvIXELR6DR4O/HKprTFSMDSq98yTciuTsxKQBklxyze+jDs6H/32aPOP6D19W200fW4Wug1uakVtA7RzOPu8dF57JfYKJI47ARiBwiN8kaBTrF0NVSrHkdwzUF2Dox6qOMRpVCiyDRXgF+OUueqqmr/9QTljtkiC544EkgfyECFb5sRb1/KVHuVHpXqm7pORxAHt/5XPDTq3UkhnlJV/ZP1VEdIahGZIE" - } - ] - } - ], - "Wallet connectBlock should set null account.createdAt for the first on-chain transaction of an account": [ - { - "value": { - "version": 4, - "id": "711ac2d6-ae9a-46f1-aed1-b233a9df1349", - "name": "accountA", - "spendingKey": "4856d9c63d3d032871626027578178e8c6d18ae520ee4663a70817c3d9c63fe6", - "viewKey": "f9b09b02a744aa8dee0af81493ff546adf008e3223150cef6adbe81e055b8010932e92de9c2a1f302b42af7c7b853eba1b0ac9132d99875752cbe57cf894a09d", - "incomingViewKey": "2d03b13b8c18c4e823cb9485667e5449bbf0e7bacb825d8dde531727eb0b2901", - "outgoingViewKey": "d91a8e7dbf4c56531ad3b17a7ab626718642c0718e69352af3ac8b06753677c0", - "publicAddress": "f05152558308b505f6f6354ebf63c7153e6d56d18f960c030fce913f60e13eda", - "createdAt": null, - "scanningEnabled": true, - "proofAuthorizingKey": "52d635f7fcecd1d8aa1f3ddee510c124dc9a0c19ce126b597275f2cddbbd4907" - }, - "head": null - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:gG+uD3IgsfpU1G5I031IQovkI6na2S19jtbdDIAsI2s=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:T6nBRFaAVNv6mZyGQIuwESeC/bmm6v9MjF175LUkMs8=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538789098, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAKBHy8tTB8r/FmK6POR7isIbm85v+JlhRIQXKYbshp6GNP5UkxuyHOfYZDcc0PnRypD5PJCdOVYvMfa2L0iAh7SMYv1T9o8JS3fMWwAQQ0WCYvijv/Bg6wnwTc3ochiQ5PIXcgLxQnmU2INXL89WL3FPq/Kr1FUJK681PgzVmIhQXllF/mExjHkZR02WngTqH3BftP60SBCeIYo1rF8ocAo/bf2Jy3H8+WWiJQ6Q1/MaLoqp1HarCOZ5ebPdn3LjGt1ZgZdKSaEMtaiv1l+GuAENWEc4CuDpqrR1LInPOwgSqiaGw/a4tJ90FmSupNPP3Dq7NkgiaV6bUz7HLU0YcVG/malnE1JAXZm3B2NXqhB3NhLJbshkTMNVcxXbswvhStxJCuy7ZKhmj5EfalZ4DCfu47/fBi/EbILCYDUF/TNS8SBq552Eg8p/1Aj42gmDWE4bHOTwsiIZ7iHhB8A0B671g3GFJ3KhSdk2uSfpo5sIkWFa65NiLP3E9jqoI9H+EEQiRAW89/8nXl48tZYQe9fLfMiQaBCti2HVfV5pMUDJmYGH3HcNSMtHQNJxLmAB9LMQ+p2TqZr0V3RzWxRC1dD9bywUfabZaL9BpTnXMrysj4gaGmUnO5klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwmiAl46IHq/guQhxT4xJWKSPMchzCvBqUBuJ95nRoztO9zm3b8dnXUBnmXP6H1bAEINq6k4blgjbcXdQfC2jmBA==" - } - ] - } - ], - "Wallet connectBlock should not set account.createdAt if the account has no transaction on the block": [ - { - "value": { - "version": 4, - "id": "f31d3fe2-b35f-49a7-8980-798854cf03eb", - "name": "accountA", - "spendingKey": "c279cf6007ca2fe2aa23c1d7051f806a9e0c5604f53c1b08a93d80b53a4e8974", - "viewKey": "7024a8ee339885cd32ebfd6378adc24eb20ab78cde7e87082aeb6591dd594970a8bfb7ba00b08d9d522f5dd9732edb92b93639763dc8ffa344079da16617ad6f", - "incomingViewKey": "2bd65c96b3906e5932db3a42febcba25d61a6953b65f523b53bcab7592578c07", - "outgoingViewKey": "fff51feb2faee0709c0dfcd8f3698408a908fadb9deeb69506cfaad625cdcac1", - "publicAddress": "388b72219f70f6da325062e5ad99e262d8a45137fca94db4de01056c8373f1b4", - "createdAt": null, - "scanningEnabled": true, - "proofAuthorizingKey": "e8977c2a73a734b44c202dd48c69a21f0031759eb4000d0ca2e25a5b85901502" - }, - "head": null - }, - { - "value": { - "version": 4, - "id": "85b515b5-8832-40bf-847b-8e550a097fbb", - "name": "accountB", - "spendingKey": "79725fd3fe93c52c61bfd98ae74097f1e4e483d5d7167ddaac60b9c7a5cd491e", - "viewKey": "dce8194d874c649b1f0abe5984b9ba60db41c6f07d2d39c7659821c5a54747476bca1f562de10898cc97ae1197b021878052f2059f1bd3bbef5106d9ecf6aa3b", - "incomingViewKey": "73bd776aa46e91e95ce5a3b720009ef9cd9e6b163bd19c70d8f5da95e675dd07", - "outgoingViewKey": "b19c83b5eb11068fe711e31942427bba2e641a03159df57e314898859c48426f", - "publicAddress": "c63c58186cc4a3c0fd3e3292fe0da1a6cc35bc463b8895da398674dab92484dc", - "createdAt": null, - "scanningEnabled": true, - "proofAuthorizingKey": "1498ae1f5c5e081d2e41309832439d8d91e2055f56caa501bdf6cbac6fbae904" - }, - "head": null - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:acRHxw+cWP/ibepfjNXZc87XBzgJndGzJC2yprfOHWM=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:wVpWK8p9G1n8FePP7dQyvqf4ypNlp1q9etiV/WQco2Q=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538791945, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAXKdkkZWD1dm5lxaxZyEsqyEuQSgM01VH/LLz3GUZ6OuXvFVUStyE3XO1pOdoWE1vVU3ffduV03pQ1RvZ7DdlEoPgykadfjTyjCPE9NzPwROPNpxnG5ZROfmUTQsF03REpqno60QDwD6QSOBYeJQzeDbHGLZUqskHx2o/DhYXQRcJT4+5tOnNmZKHMzjOA0DGjp1z7HeL1hPSGv8Z8RTxH4pwE4Z053WnD13onZHufoyHVXT4zoBh0raTcRDEDoJ2JXoWvBzkCiq3qefIvWkNlHlJ/zxVvrwA/vEOnUWmDkiWSDtK1L64iGgHBaZOTK0pC3eqM6cMyRKriiJ2FVfeNBb8rOTA37uWvGnUBXP5YWrJ7p3NdcGdl7djHxSu3ooQ/07FtJTTY72T140KpBzDwOGplWx3Y9P+9E56YaC4/Kqt9Sne/KX67k12dnWybklD015yAz+3LqsifgagDl+wxJawQJw2us3STYHSHXvkWanRMwehkzknDf57pHHRmr+qgTY+6wMstxpJR31lkfCJunBjC7kCLZn6M8U6lBc1ew9+asJcpDo+RFQMs8/YKbfr0Gmo2LjAGPTF5xWnDdsL0glR0uT8TUUrHA8w4mwSDUAVdnazlEcSkklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAws2uAaFTsGEJtmdqVD++dkjMHns212MietZsizjoFcHJ7znoxEVUB1I64k4kGb8/O6MLCWU5nD2S2tHBCz4cuDg==" - } - ] - } - ], - "Wallet connectBlock should not set account.createdAt if it is not null": [ - { - "value": { - "version": 4, - "id": "dd451e67-cfd3-4ac5-a488-b27beeff4c9c", - "name": "accountA", - "spendingKey": "39811f488f54d6e92d3f7f341cc5b76f574a24197bdaeeee6a05de53ddcce83c", - "viewKey": "e4b08d1947feaa48eb780fea468d7c493aa3a5cfe02d7cbe3119b76aa62dc358273e68609510a534841bd424f7c20e2444104bf04e4fc3706c1259c3725fba4c", - "incomingViewKey": "90a8f8f98471821e9df1412e1d950a2e6a5ac1489e433e79236be37293cfa807", - "outgoingViewKey": "801e2c3ebcf7916e410adceaaa7ed4acf98fa6b8119ffe11f3d11cbf45ae91fc", - "publicAddress": "f58e0e6b8457208f382488f030a45fe5415973c80c6929a2393c06c115f9a94f", - "createdAt": null, - "scanningEnabled": true, - "proofAuthorizingKey": "58652f234e80b7019de218000e350eaf0c711ee5dd4c66d72f1d611ca25ed302" - }, - "head": null - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:elUGwi/sjfDIPJd0ErK88yK0AQSgPV4vYjDWsZFx+DA=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:Ddn3jbvQB5ORKUEEfn8s/xrMGYN7gpmqXNqVmOcwoxc=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538794458, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA1NqHxiJj54BQE17UDEPqVMbiCcMQ0nGmWz9At5DWA9+1xNMsFOEsMnZ54bNvm2I6ppVkewODOurdu7HnwtI5nmWO6REWA3K+feX2bUnyLa2EIBrTpMnt2XOJ98A1u6zVEgnTY32iAZ0dHpC8PGOlLFx9pOR97x6Z7Z6AsATZIxcJeWwgfsvMsNBWqEvoPonu8R3NQWG23Fe2TbCigyn+QqQldKjLuKxWlVhg1NuyFICWDBsx/7K4KKItSyJz7cj3F7TxT/9yW1/eb+wJkY2JjD5pzDeVvRpKvA+uZCAPeUY2Rwz9F0ddrKkwGVImQdy7ZUOb8e74pIwvfrJcPn2Qk/6vMJEJdAXHdrIN0SANx69VBPyTFk85jibeo79uYzdbGcJhFlPYIGvmQ618NJfPKEv2E3bc2c1eAYsjbvl2mtjprKllsNaxwnd8outXYfPFum/neQ+LaSzNQupTWcG0s7pvIULijYPZNE+dr8sWOKQkDJ5TYSS2m4mDgDmP6ZTmwyI2Jf7IT0HWiGWjT5vSy7IXBtGxRb3bEayCHn5PlkLF0f6J1U3flI3fzmNvya1dlD07qJn4SMCSxt1VmoxxH49bO+20XQe1llZS3LLBK8vFRyF31xgzpklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwQqQfM5NUTAIrURgLbUaJ0oIwwnOHaDjnI/hUQRPoKdsjmBO7l9v8F5f1EhXUe9wt3K68vq7WBtRg6ETHKQUtCQ==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "04D49503415F9C55A0D0757A7E6207923076EC745B05AAC727AB61E1B9C8BD0C", - "noteCommitment": { - "type": "Buffer", - "data": "base64:rrdnmsHXUQh+6XNX9knJnyOOKXHcQi4/3cmG13KpKD0=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:lE+nXnuSQZ8baWE1GYfBUo33F9o54ptpAAejyKhRaXg=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538795622, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAAN5H5ABA6oSm2IhIhHvcCR/kFPX9CPyzmK5Af8zgnyuVNtfT/Q0yZUc6YU5+aGFpz1hlZp8oKlKHdnAw5MpYwG25ZYTYT5MoRwczDnPKm0SuAkn/pRbcdQhu3lD36LjvKF22IhZNAVjrZoygkgf0yHYIjoPZc7NFTe4z36tytSoPKfPqkfWmCgV/jRn3C98EDuW/8ZDHtUdfjucRk36aBPxynQ4SIoyQ9pGSE2GNa4WXY7hk4L+nuf1bsCpUzy5Mt/cff29rgW5giGjrxCfltnJl7KCSe1ujCxXyfbCHBsoaSWZSVYMFGiK73oP7ApJ5GqjRNLXkqlqtOPw0VX9xOE1dnAJoWw/K/eKWl4a10xW2LDZ6pOgnRbA/oCocyx49UQyFGW0V4o+9PYA/H08QhtDo6HWMl0sPZsT2T/dT/jSo7kHVLuSgOJm8sEtYxiZrM26/hHsAiiCUHadqvIm5yRzA7+7uKWkfj5+YIRoedOcRPUQieqwlt1ZtyLEmxPN79vX5QrAqDDHmOJrTF+0C2xolH21pS12fDMclRK5qUSW71q54PSUYXSHWQ9y0Q0M2fUTeLZoGOkKNLosh0sen9dE5o7GKkFeNYSmaSRde83LoRV5FJDx/mklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw21ZN8hjtH82tG4OEQAEWDVGcXfmUbmHhAqnYZ72n+RdIFAKR1FzOiiAjuygEWxfSCjvra2TJ9DAApeMOkO5NCA==" - } - ] - } - ], - "Wallet connectBlock should skip decryption for accounts with createdAt later than the block header": [ - { - "value": { - "version": 4, - "id": "b588dd36-a52f-43c8-ab8c-ed43a64b76d7", - "name": "a", - "spendingKey": "9058f54f590f4453d9044d8fd1dda16ef29608ec7c79c71ae754c452b5228b85", - "viewKey": "1dab86894e0b022cd1d2d547bc2c75524013006456bb0a9281c94dd38f866d4035c70a7bc98d4d2de2dd7e3d143e30790a0efdfa148c4bbe01515f441d08149b", - "incomingViewKey": "01ef479369c340c617803aaebe5749807fc5ba8f24a44f2f8efdff2f57e37203", - "outgoingViewKey": "2a16e7d2db5541a55d55bacd69f5a5077d3aca6d64482032a74405042dbe23c4", - "publicAddress": "002e67c1b367a11cfac003e4c6a22352676c6db67ee482a6cf425614a3903be5", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "bce77800905da0e98d479dbd811ac2b941bc7fe0743093aab2d1c2d5548cfc09" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:mNWDk2Q+ISdSRIMDHQ28QKill4FoO2jg1bPIFRNvtHE=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:KRr7EGRKjnQqKLwz7R3+n296Fz9F7B9+nY2OS6Ly6Ew=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538798786, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAowQEtMyAsMn+9DffGKM2ND2vf140nq3sLf6Xwdml0qup4saqCdK3HmsxhTlyEab4Ni7dYjth6MPPHyCN7tGxiWdvkdJ3Jnsb9tttqEancsuunx9wZRJbIQMkv5x7FXs54MG6Wiu4B0Dd+/lpw3oCC7VivZ/gLXJW8Yic6S3auxcEXFb0th1QIC320Ii/hGpSiT97UcgV4ypVilS6t/nSinKTYN5ZH8ynHsnFEY+45HikMnmu2OOC/E8TVWLOsI3bZI/fKkm7CWZ3wiMZtMGoXNbV5dywoLzWFah1q7xwusMRLA3k7/UiCk0xjdZJAcNZfa80OPyld5/qVep+tDkrmL92vFg/H7dupkTQbfcOB8Dz3gNOMwFgeuk/kQrG4MhNNuN1Qnrn/3Qi1TeemwBOkDTD0SQ5ZrOUvApZbBSImZ4Bovy4FiLQrJ3xsyQjaPqHOB1r+Ygi5UU2NBnVGU8S+w+BDa6AwKGy97fmNJ3Z+sfF+FczZc5C3s6KHUbNYxjWP5MasyiCokewZJ6diYGl6x5OAxxyD6MleaOfKjpYMyJgSM+HSmuqbGvsbBmVUK2fSIm5o+x6P9460RTd+vhPzQ+vwKrPHSug2gIl1acompD+BdJV7YQel0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwqFSGvY54V4N9zdzKv4B7rsVbhoZ0Mg9I0bDGgSuHJ2EltUbGq+ZvBmaiwnLwBKAi7uRxNyGzMwV3xPNmXmmQAA==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "7688B6A5D7E665E658526F7ADAF4FB5BF7CDD2BBD99341DBB6A6849CA3CAAEB1", - "noteCommitment": { - "type": "Buffer", - "data": "base64:46ARjbh7IqMrWxqcO8wX4d9GcBVw/R8/yLVycfMxwh4=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:ypsgJsKXpigzL8WH4wapmG1AzuyUFKLp4qRqjQsKEVk=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538800950, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAol+BoW5SFHzxc92P1NR4/Ybi1ogYtXhHsUi7UvxvOLCxVomyK0zQqi0y9mye0uNu84tmRuB2ggmRiwlEQ87m3zVx/shoQ4pWaMOJUx6h38qx2iqDkDMW6L1bflvSpDIfzQi8h1y+/WLFm9n8hQTA114oKJT8G0By3mLWsX9catsUt/FxnHFNKhZNXym8M6fdN7IVadXUi8kBe+C7HsDOZtipfcGM7wu5ropsxs3mswC3GeQEgtdBHqIxFkRiqqhthdZeKJZN4+biXFQ4Mc+qykbNkaXqtVnFuACAQjpse7vN2PWQuKjMxzMgTOuQ8Hg3JrRm2k9+mJidofvHvqZ9YJu9wRu8vZIotnl+HuYEnkSMSKt4ZlrawRHCuu4uz7Bo2H11Pf4xrDX0YmWhLIT6OQyWj40ZRrbuIuJoPB1Kl+eg1zRJHOo7FHoBzxl7G0LYzs9hNBhzbAG26vLjD/Jhfxkfm1ZbhTYzhPY4q4vD6ZRrUrOhoqNSkK4Oub6vo2jnvJl9wJ4UnN3aQkBjkEAAVQcYTDOk6tsEVSXYHvImmay0MXiLKzAf2ThI9NmpOLm6FMhR7R+MtnDRZceWYq4VnGfygFu6PHdzQv5RLpVL09ZUpur1qEJ/5klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwrZHUl4EkptmplQte1OhdaHPtUfOkDqLxm8ucjAZ0htLU8eGoNDv7LOhtJqurc+u1plSWFvBvu1aQnGWAKQ9WBg==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAU3NULCyyfGTLdHPRcjrQu6xvlLDHRFNKQjpVRtnVe0aFKPWfudzbpib8u+yazaCbxvmBHvvnU3Tf8vEF00ny33Lgs1Y6bIA54mCbacBeT12recZHGrkqG/KW3ddUYwMxWb4J8XEaJcpEjX6ZjHsJUTz6nAtRA2K339Q7GJSQpvYRrzBTxwpfsrmoerV3Amn1BJACOJDGfAqQSqfLcNY8gPDB612FWTDZfq/hBk64WeCKyLL53ZuzHjQlpwRhqpT4Bmj3h4W3RoSFE6dEA+16ns1bdWkdG6nPWWqYYpHRlJziwkq0n4TBICEn+yrxWCJkWhAXjm/EKCndP7v2N8Qr8SH7dmBmcYSGmuEwRmN18CJJjim0xJAh157fy/mSRCJnc/FBr9plEGUmcA72m0XCiDOXtuFmOsWHoIk1siOcWFBA31MMKzsst+IrnOxX245T/co+fWCTOfWCXXQrmxAdB+iAtPmzgmO8SAlA06DJmDmtBeUVHb2vsXIXZDJjsM/jnFwV5c2vD9VeN30jf2WGwdZlOsdZbXj/59kWhruEWdWZ3XthLQqRki0fLoYGwn43HQblYP9RvwipJWmR6a6nj6ryB5DiGJ0NKWZ0lSlMJ4mJ7lw6WGUuHElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwEbvDPp+RDW8PbuCORTKWYb3bJycg1pljPBwWGbYnqt1iumQhbXVkBooC4EvqlNL2gNOd/iaBJ4FStugrQKLkAw==" } ] }, { "value": { "version": 4, - "id": "99316e47-0acb-4513-bdff-26ea481ba956", + "id": "7d4d70b2-6ec7-4167-9151-f1438c7b091f", "name": "b", - "spendingKey": "23cfd3578bd734c519359510a4e314d6aee03779c8a4417027a1724bc2c12a75", - "viewKey": "e0045a5dfdec70240386b79b6c883175cb3e14993d9eb6a375fc4a88769028b9fce06cefd661d9be699283724f8b734313b2bad44e48432dac90fdfe251e05ee", - "incomingViewKey": "d93d88278fb58013b1ba30bfe8a00f4adc907274fe9f91c5a53e61acc4f5e003", - "outgoingViewKey": "4a6df989a0340102fef6a71b03df8cba0e0635bee1963791fb900b8dc705e383", - "publicAddress": "40140a86a5071c4985e1ed66c2b8a0682e9a60ff45cf51e464f8820db66dc701", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:zlABkHbPDQPcYhD7AzaIX5QOulfKN5eNG9QdRHEh3RU=" - }, - "sequence": 3 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "22d94ff922e50cd7fe9ae0c514968f3e52badb3d1e835ab0975d66e80c398801" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:zlABkHbPDQPcYhD7AzaIX5QOulfKN5eNG9QdRHEh3RU=" - }, - "sequence": 3 - } - } - ], - "Wallet connectBlock should skip updating accounts with scanningEnabled set to false": [ - { - "value": { - "version": 4, - "id": "55b5d5c0-c65a-4793-abe3-60db528f677e", - "name": "a", - "spendingKey": "7dc498a9da3700f32cccb798f96367bd43d5d8da827f13142a97da632b47398f", - "viewKey": "47ae04369220115e2936d54e0c900c8779b96b0e287f8de747cb383e9d53f3bc1a688b4c672e5f59d09447d964c2d659a87b9db6b3648f8728a68b8f52fc85ea", - "incomingViewKey": "3f80e3b0bf87fc10a7823f349b97858efc9c9ad62c3d5e3afe76f2eced7de002", - "outgoingViewKey": "4da65b8bd524c0f9e5b6e98998ab575d8cb912202f42e9ea73e1f4c1e2ff955b", - "publicAddress": "17eec804ae83239ae38c8849008c0864f2b4bf1ff70b341e71e7a2b126d079b8", + "spendingKey": "dfeb5befd86eeec4a28484a98ae0945bef3d08d66820f23d641248d455dd8a6c", + "viewKey": "ea99d3ba32e2afdf7868ec05b672a611c7027fa1a0c2eaf0e46cc8eca047f14b8ed9d2b0980196608ff44f698b8b4f01f626a79bf764fc64d705d8d0293dd236", + "incomingViewKey": "3b7294d5f761708107d3de2fe1988055dd54c68e4718af20d7fe1acfd7532702", + "outgoingViewKey": "a9186f68b889911d78461e9b5576aae0b2d97547ba4fe809b36b53a53651ad70", + "publicAddress": "bccc050a0c7444743fb01d59230dde81003021d6cea4d8b239afe021102463e7", "createdAt": { "hash": { "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + "data": "base64:mZq6o8uyv4LYoSbNLuuK+fF4tYM65yBRkmI4JuPAnok=" }, - "sequence": 1 + "sequence": 2 }, "scanningEnabled": true, - "proofAuthorizingKey": "b0020a642ac5a40462d3d4cec61e1f2701d63385ec3980df5916e3fffee4940b" + "proofAuthorizingKey": "43394e75285fa9d2c1785fe2758c26820966f0e0d38803ce0b21d8cb406efd08" }, "head": { "hash": { "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + "data": "base64:mZq6o8uyv4LYoSbNLuuK+fF4tYM65yBRkmI4JuPAnok=" }, - "sequence": 1 + "sequence": 2 } - }, + } + ], + "Wallet resetAccount should optionally set scanningEnabled to true": [ { "value": { "version": 4, - "id": "68cbe0b7-4853-4798-b33e-718da60abcf4", - "name": "b", - "spendingKey": "c4f0d8703fb0adcba6556381d5ddbc90bcea828a07283f60829977296c141a93", - "viewKey": "707f8bc5c6e5982169e70c0f2c34c23f16d5b7d3423cfffc75d17b95850587b63d25ad7939e2ba0847207472de32edb348560aa86aeac2147be1474c7e51ba6d", - "incomingViewKey": "93050c8e56f3b603eb59f5f3185bfec01549efe873acfa463009954e3ad8aa04", - "outgoingViewKey": "8436e444dc23a2d8d5712f536a14ac56f0cc27e7aa691602d8c2e5fcf4ef4ff4", - "publicAddress": "a085e2b5c5beed24baf419e0d5d48d1c28acc6ff4aa941e6d75f509450fb888f", + "id": "c71a1138-638e-44fe-8058-6f61c4837c63", + "name": "a", + "spendingKey": "ecce415425e647d632f625effdb1717428b14a35a8c29bf1d8961b028be538a4", + "viewKey": "f9af8ad35f04c95bdd6b84694755bd5dc5cd92345f8262a33558632f491e145970f01a3d9e095287f2f3bc8f1a020799007eb914e439842918857737d89d8a6f", + "incomingViewKey": "368140c29ca137998fedb7e6e51f20411db9dd56a796764d9e361590ed23de07", + "outgoingViewKey": "acd19c3801777aa4fd97cf7819d08d3bece3d14b83245fe29b24acec1bb4fe96", + "publicAddress": "81ac0d338d2048bac791a3cfb92818638d183465c3fc20d24dccd73e0e442d8d", "createdAt": { "hash": { "type": "Buffer", @@ -5818,7 +4642,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "252642d09a7431a7e0dd31bbd9324588f794a7ed55154a9a8a563d2e34be8407" + "proofAuthorizingKey": "eebe1953bc0241b2e20885684b0c533e110ee701b2efd97afea2ef00c999360e" }, "head": { "hash": { @@ -5827,71 +4651,19 @@ }, "sequence": 1 } - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:T26lr8PMOBcM5kYMOUt5cU1rtNnix147uTQVXz6+ExE=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:3mxMJXiCGdQwkhDJU9bwv2i6KA/ssRtV+Ly+NzXBGbo=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538804397, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAADnV1BhIJXegnwrktCtTYJf+B0PuPJseZ0kiIlvjWjiq5nTnsx3G7M4yBsFjRG8j5vNRq6xkQAuVUnKOIEV6EQNmfzgN1FXk6vCf3spJHiFOmXmWbwJJ78y/JgQ9XjrIty+gjb7gBqWqssrRUIv2tvgniiWDA8O5BYHrGAvuJ0ZMVD3hlYSu6t42UDBcy/gwbu1bBU8bv9nm+2UpxkozCFB4ePUoRwFeHpeGuGzsdKQuAJXj9gdoHAXJHId8q/883O4vd6oBfxsMWQdZx2nLW4QUtTUqbLr1tCZxqDK5ExVLAaB9pAvD+9QJcmE6b4mSD2x4QW2XSFxCLtDdGAlyDZkdU3RADN18ibp2y7B5+r5ZaV4EDGBCV/llNPgvpPb1rRTtR2kVZf11WWAnH+s4O41GPDNn+jKDk0mxSmShc1JHpgmAF70WBHoUdVshH6c2iGk9IaLG56bnq1LQKofHOKYfWNnVSwVqzBga+viUzFLXx1f2ViMJmudYDCH69W0veHCZSX08JiKbefZsPAfTc9HDP2dSY21WfFV2fRJU/fLRzD9lkGUei6kElqEiwW1oUHPlwIqhY8IcIEhI8dajAoeFEmGIzIfrWoTTeEqxdNN3533sYhCRieUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwA6QUF5JVn8fPQYfsu5ypg3+9oorQF3XZi8Rbe1ULH8kWP/WV76w108cpEF4n6KGuWv8iyHlAtOo26MQMk9p9Aw==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "FAFF4201484462C6EBA2A87EEDFEF3EAD15674945B9BB92F6821A6A8D82433C7", - "noteCommitment": { - "type": "Buffer", - "data": "base64:XZqMUKJKccQbWWfMk/OvUyo/srN6DS9FmWUVb5KxazA=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:RB+cuH59qMEU3YUPTGgRWNMOds4BdrEw6Y1D9Byuc+A=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538806590, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAARc3xGXqhfWxUC895dmLdMkdi7JOTAosTuTkwHgxfQ9WU1jtHmkxEswUmgx1nKxGiMvl3vkuUFBhpL3lRriNDylUsNyoXbNCdDEPsbIgQacez0wkI7ZlE2ii6kjwU66xgpYCawon59mpaHkSDght/i1vdTn1hqgZiptFPXztNdHwMG/EMEmvOzGlQ4uEDFMLVy5ah+6o4Ts0OkEuOoEJY9nQT4TqgX0ExaM4IytIU0g2E2JzQTBMx6sLbMUIJX7bpoqbf40YqXGTXgEmb2zQw5B37loviIbV36360CD7Ld8QeJavNc6Knlf82MzoivMAhjLcF6gGkpyaKwjM0/oUmxR4Or5SRFOcOmiPJq4TLdQu74xh4LwuvtkhW5O/PArthSFwrrCqZ/uR48bewoQ5e3k/hzwiisubBWMKxzcITzJbG2D9IOelK8jupaQ0curYUpWxwIeyeqDk0fE9K5ZkhJ/BrqrcWBfCs6g9hKmG1fb3TpUvl46hD7cp1rLt/lbOMS+uS5TfwivGbzRHkMHqB+XhSlsHKSXqEEIEtc3avxSpI+Vcu8XEv2JXRAGD81MLzhM4uNn+6DFnbfA+D9Fef1cXB+bpOv1SHTKHG2ZOSlGwngXCuYwQkBklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwjbNX4ht0TYLHYdEzOT6GmqBDanqfUBNoG4EQb1G7rzPoDl/pK9lyqSjAjVbi1y8NSD9dGSWj37vO7P9FB5x0Cg==" - } - ] } ], - "Wallet getAssetStatus should return the correct status for assets": [ + "Wallet getTransactionType should return miner type for minersFee transactions": [ { "value": { "version": 4, - "id": "9ce72f1e-3ece-49cb-b65d-74f015d8868f", - "name": "test", - "spendingKey": "7d1b498c8d3cc4f73e73e6fd478a9365b0d8531ff6164c02ea4a082fc4f62153", - "viewKey": "ec221089ff616478f18ac4407a18ddcb1df20258cd9a573c50e16a16b5e1e4ec78d86afb3a27ad9cc1e928c20f0ad5dd983a1ab1043962a4679cc95f0821102b", - "incomingViewKey": "a133eac24bf5a25cc97053c90946ce9463c4504cc0cd1ec5197c2e0ffab3da01", - "outgoingViewKey": "85720d0846bd0c593b496c79637b57e4b6a0f2ec525d157424918d23724d4bfa", - "publicAddress": "bfcc2c07f689708132f5bc4e04d9457770885b1835152dc8d5a78a11ebe32eae", + "id": "c56fbe5c-2319-43c6-9c74-6957bd458e80", + "name": "a", + "spendingKey": "e64dd4e579c36542e7bb22de61769f11119a7449f2a28a48ce1461d8cfe4ea29", + "viewKey": "c9f799ff86541fec1f0d242c4f48056292ee077d739b5ac24a5a2d9636bc7d0f05522b614e5300c3bfcdb64f81d19390ece894886a632128503c66b644d4c452", + "incomingViewKey": "514c3812e86bf59e651f35cdf5797b41d1c3dafa1d9f3a6005781b18ac9ecb06", + "outgoingViewKey": "702f217dc84ba67dfa4bc09a3c8f6491514c9b5b1b05891ec4b9bc762ff9d4db", + "publicAddress": "60f29d4d089cbefe816d8892bc7a9e39b14541f4e8ab805819b3381ea3cd3d01", "createdAt": { "hash": { "type": "Buffer", @@ -5900,7 +4672,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "9da0cf11902fe7853a0efd7737c8d409f3f06ad031eceb3f0bf2d1759431b904" + "proofAuthorizingKey": "03aec8ded54e30e5422791974a8fc3ff5f0b9ec7ae7ff71b599962a024983508" }, "head": { "hash": { @@ -5916,15 +4688,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:ljyAKZkpGtZzzVxsP+MLmB+LBKtd2d46th2i6C1y6yY=" + "data": "base64:D+WvBzZCzglfbki2aW7CExBQTYwhthCDqqGeTJNA3AE=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:nCDFbONWvWb7DMJ9Kn+enFcxmO9FXei5rvmcHC+1lzw=" + "data": "base64:tZDwrof0mSVCzaFO/8KbpJjC4UzZlApxhdkikVYRcMc=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538810002, + "timestamp": 1717538940787, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -5932,56 +4704,22 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAIO6f0wCjg44SxkFkq204mtRvRYfkZb9oiVMSqrTUa0KhJFzHrNkZmy6ZttvdzFoXhEgdjLVRTc8x2ye7DRcF9JtLSl8KlA1XxayVNFtFLsaM1u3yZVcSN1YtLI0+woB0Zdvkj6CIPVXruZOiMYn/egap0Z0br5EcRbCTVGTeST8BO+8r1mzD8DzMqoAQbYG4xHwg5NcO/a3svvTNELC4pj9KF55LHOhTUccRfsiVPZ2P1OLx4A40LjNN8B36r9XzkPDeqs2QpWDMmCDFvKRmwA8pR77wylHzBbCNDxhwdyYlRVmmGR2yOP5Z2sO8/phrXIgasxp6z5ebCAFA4OEVubwnQ28zb3D/kWSPgSIvNvo04NoguTK5np9cySOTDTBNv06RwKYNE9jq1834s9i9pUZgEv0HmyfZnsZYJm9VyTk2qrsWE+wRTMT0s0ckWpqFJ/RGhj5EufQ08Od/6YHXILVD7XUpX9SG3Pi4p/CgDWX+22wIXR20pcUunVZCcd5C0rH4q613VtFU1L3o5qs7sQLNmH+x/15mnrdpAmpB/j4OhyH/VpQsT4skZ7BRJEQXPAAvHaB/zphGSD93D20Wf92yRKd95QYWtaf1Z/y+R6AH0cUHmXm8KElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwfB0rqqpXN4dcLtAKd1ebu62PbRC8xb3GlGApQjCmqQalIKDnOOrbq+bktmrbJUYbpdiwnU2VI+h0SrlS72OMBg==" - } - ] - }, - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAx1XG8nmZYHQ1lZDF28yrjNdwPtLmJXorkmHX9wpd2smEGUFH1c+R1x90Cx1/C++ZDaG0LZOzFoURhJxAgkQWtLaKuHgy4X/THyeEIRF2de+rikJf3pai7Sl1Rk5keZmyEGdca9g1R4j11EvorfIueXMu5tUkSxjXGQih7xj00BAEzGD//L2AFiegE3c7DNOXrcmry9GugoJLx0NtqS+rSFuQSJWJkifCzus9NzVT5DWDpPIC1IiQe5v6JhhNCAD3bawctCBU4KkpKbzeJTChU9EJ8Aexj6eRDpAlecHOsVGGCsJ0dz1bBRkJiQ9UnGQ2IaU1CRRecDT3y2tEEZh3DniqfyVpGJdU6HL5I793Vf4K9pDxdBYAzyBR5hkQ2qMrPC7D7dcJHAmBEEGkBPlB25ztk1sUmFJNHlIq/E3LJm3E61ASn2x8lz7VW0X+J1CrzBetBuTa8P1WyM34ygGinGtIbuixAvKTuBPD3D1WYHUL8AmCSLBd2MQVXQ4QXhkytGrV71vL0Y9TIQUUmsynj1zfwRDdhGwl0JEAQKEaydEm/MGpIgKMunwX17UIT1DcRHZs8QDL7V5ddIHkl+pamlvYOqTG/Uv2yY4gt7+/5P9cKsBew8hqBMnYroNLOiCPvcwZKFcQl7wbyc1xBSswaeLuNGt5TtT27T+9Dr/HzejTvH8KfCGfBD68uJnPQsK86SBZQilhh6ix2IGJIsMAh06kCZq+XQoBoFkdkqFZd+4RcgcjGpbRMA+YBC7PguW7foeHd0jEiouRNB9IcfndU8o3VUtdabdKkYsF5F0XvJ+V1+586fBd1gJ8P7HZaCfXlVbvyTh8vjC17V8G0LSxSdvnpKr7iOgvD4l2mFXwK2CnEwLRVNAVR0j2WiUUknP0PDm6N9u2sf3xctF4ldJ/EsaKRN6kDSpgooqKnc/d6JHoWKN5KJj/MI5L6FnZzO9BS9sesUewkJiNSpAYd3hvVuz4C2buMGj0v8wsB/aJcIEy9bxOBNlFd3CIWxg1FS3I1aeKEevjLq5hc3NldAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEKAAAAAAAAAHegEyym/Ucg/MHeuws1hyy+jM+zhif831l7UTldqpaTNPaIs2r0vnhH5D+wIM5HCifg0OlnTjV7G2mcuziyfgc9gwv2Dr2csHNTeZsubKTYJwSSmUTRfzwujsxrJaaUv1RZoQ1ta5XLLCMBXq33YxSofd7+rr6rAi8TArKGCNUK" - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "2000D5E8FFDBCD7F4A0B1ACCAC17F750C344FE3B9B927E088269693C14B28C0A", - "noteCommitment": { - "type": "Buffer", - "data": "base64:8S62RRIAfpSANrCm3IxZu3YlcBtQJrQcAQJu1v5QdRs=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:+NGczX+1gjYFWQjmPo959KmEs7QAsV8p9sdVuJc1bOk=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538813921, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 6, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAyRShcULppVi9LURNIeUTgqVKs5/2NBIGXDchmWTbDOGN3WiWZ3tk4yaP9BckHcup4FbSxtyVPNJa0jiHdFppxUAxRBk687ttGhAWVqkY3ZCZRbXybGPSW9EfrRL35y227I4qnga9M9DK/9m8mFkJ78f3B90P+ILAB/HZwfysPJEJFIEiX1O5pAAtcSE41CQj4NUzKyn0ReNwqSC/+sp2jXjdCMmcrFlNR68q+ppFbQiLS/7EEet+3SZO6OYLF1N5xzucRYwtFSWtCIQb0HlKSj8ObuJMX3B4oaiXlKaEXX3VRnyRdQRJwcWiGaPrbZJLjzUVt3fEE0Jq6AnJNjAjrp6/PW27kDsgDAyZD1Qh+xEFNhPf7pUmTb1Kgk1dD2YTfFx6dwb5NwGaBTIsQIKano473Y9q1fFWMLBBKSD4JtfuPRo+aN77/LyMuBkScG/Rs9eT//pEhf/xnPW1D+G45XG4CF7q6TjhZOkWJ7/IlXnnIskhl3f2eS47zvfRNZHrGW6ZVEuXDF+0hlbopNX/SFhZIU7n7UyViwQYnU/t9WLaDm2G8P4kDn/7ugpH6o9geblFd3P+YEkx1mZd44c99XsXpjyDL85QiOia0B0CylYQc4T5kNM3wUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwSFe9YgIoJjUXjpiKrnrwtdncEAuFWPoaUez72Nto6unZYHBkzsMqSP4/g4Coe29M2MlNEBASUzWnYD18meGKBA==" - }, - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAx1XG8nmZYHQ1lZDF28yrjNdwPtLmJXorkmHX9wpd2smEGUFH1c+R1x90Cx1/C++ZDaG0LZOzFoURhJxAgkQWtLaKuHgy4X/THyeEIRF2de+rikJf3pai7Sl1Rk5keZmyEGdca9g1R4j11EvorfIueXMu5tUkSxjXGQih7xj00BAEzGD//L2AFiegE3c7DNOXrcmry9GugoJLx0NtqS+rSFuQSJWJkifCzus9NzVT5DWDpPIC1IiQe5v6JhhNCAD3bawctCBU4KkpKbzeJTChU9EJ8Aexj6eRDpAlecHOsVGGCsJ0dz1bBRkJiQ9UnGQ2IaU1CRRecDT3y2tEEZh3DniqfyVpGJdU6HL5I793Vf4K9pDxdBYAzyBR5hkQ2qMrPC7D7dcJHAmBEEGkBPlB25ztk1sUmFJNHlIq/E3LJm3E61ASn2x8lz7VW0X+J1CrzBetBuTa8P1WyM34ygGinGtIbuixAvKTuBPD3D1WYHUL8AmCSLBd2MQVXQ4QXhkytGrV71vL0Y9TIQUUmsynj1zfwRDdhGwl0JEAQKEaydEm/MGpIgKMunwX17UIT1DcRHZs8QDL7V5ddIHkl+pamlvYOqTG/Uv2yY4gt7+/5P9cKsBew8hqBMnYroNLOiCPvcwZKFcQl7wbyc1xBSswaeLuNGt5TtT27T+9Dr/HzejTvH8KfCGfBD68uJnPQsK86SBZQilhh6ix2IGJIsMAh06kCZq+XQoBoFkdkqFZd+4RcgcjGpbRMA+YBC7PguW7foeHd0jEiouRNB9IcfndU8o3VUtdabdKkYsF5F0XvJ+V1+586fBd1gJ8P7HZaCfXlVbvyTh8vjC17V8G0LSxSdvnpKr7iOgvD4l2mFXwK2CnEwLRVNAVR0j2WiUUknP0PDm6N9u2sf3xctF4ldJ/EsaKRN6kDSpgooqKnc/d6JHoWKN5KJj/MI5L6FnZzO9BS9sesUewkJiNSpAYd3hvVuz4C2buMGj0v8wsB/aJcIEy9bxOBNlFd3CIWxg1FS3I1aeKEevjLq5hc3NldAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEKAAAAAAAAAHegEyym/Ucg/MHeuws1hyy+jM+zhif831l7UTldqpaTNPaIs2r0vnhH5D+wIM5HCifg0OlnTjV7G2mcuziyfgc9gwv2Dr2csHNTeZsubKTYJwSSmUTRfzwujsxrJaaUv1RZoQ1ta5XLLCMBXq33YxSofd7+rr6rAi8TArKGCNUK" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAlzbVRjPC61/JquWjwWyxyuF8Aaxg9sW3d6vGFmVfHF2tIu4Fml71ATpmI0i6X92Fx9irMPI2CIh+UFgO2PmR6wsYv2gWaT6XQURcSGiO+NmB0zXFRDq4boO5uuSRfRwitcRjKhO+b3uqlLLOWzz61l1qjHH/Qpj+vio90vrOQgcCwMjDNE1P8fOhIQmPOhf46oixUERd/zPqEECvaVpml+4R2lYCwJJoYKvqqP1FL5+1ufTRdMr6YQ3g3KuJJSQBCepqWzFiTInca95LI5LUVfYsnkj7o5pQjG9FmeSDWCtTC2S2qInyEMFZDCGmbINwUoeROqz36qIvLPkdwnEs66gFaV6Ki85sVtxBNS53Z2gJN5SVLKqsoBhaEdY8EpkwjbgQmEs+lRsAIr2H/BnTpAr+JGTEQKTpyS9UK8jIOV2SHDAr54ogOLdQU/t6e8rFmvaMupO/zMktpLWXU5Wx/B9JFvKUTY/M3nmdhDmoFvzI1QGZklfTPzhvOqLVZmmZyOEZc8yL6ciwWG6g98V5Bw2l4dJvsziVSZskXhqLdHmmv9zqH0ER0I4PDYA2kOvbgRAZeoddLkWBGNbJgSWhOM8AqkaxanwG3NaCCkFLx2Y9pwjSz0YUiklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw+4Y2WOP5osklh8+xp33c4C6rsgG4d5R+Qkd4DAI/eICJfe4OsYSH7BQSSMt9KcOdZcT1/zgYQNkooCuo8LewAg==" } ] } ], - "Wallet disconnectBlock should update transactions in the walletDb with blockHash and sequence null": [ + "Wallet getTransactionType should return send type for outgoing transactions": [ { "value": { "version": 4, - "id": "4d36c81b-117a-422b-ac3b-a9313d02b5f3", + "id": "64e75cf9-efd5-44ee-90ae-35c791638917", "name": "a", - "spendingKey": "6a6e0c3cc54799d623f7d9727eaad86529db7c76b1b676d1a2059ce3dfe4b793", - "viewKey": "d7476ab6c2b44fc2a86f9f2e3229465a156e8ec0545eab7351b585b1e26b7568c5f5793362cff416f7c9ae66a5d96fdd44562e634fa150c96e177c45163fa04e", - "incomingViewKey": "559be50c376636b333867940c56e5adf3bf14102891c12e2df915bde0148ed03", - "outgoingViewKey": "fda80687cb6b89197a2f474bc395557aaa78e8ee775375a65fb46fb9c8d4db8a", - "publicAddress": "77f8f0f146065f36329224ae5b3e3f12cab487ca04d4f589754f97872dd08e2e", + "spendingKey": "846f74914a7f7fe27a35df50c4459a65af4b8d1317638b5c7d08ecff5a894e07", + "viewKey": "df507062f8bb6759ff5626cec5a59ac49a173906e94bef23ca185bdfaf3a691e918383a46293981fb85b4971a798c56cad7315f35b2aa11bef4116f449aca84b", + "incomingViewKey": "15b6ef66795b5a640221a5fe863b69542ca92aaade03e33d4c828b1aee050205", + "outgoingViewKey": "ee464e9cc173682b92fe04a12db52e03e8951d02d58049776c774c450cebec77", + "publicAddress": "fc1b87236e98d329028c59dcb55139dece067d9a8092fc02f92bb83d5afd6cbf", "createdAt": { "hash": { "type": "Buffer", @@ -5990,7 +4728,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "c12d8e4220df0aed7a6089a41f1b3f95198e22fb300fa9a81737065a9e235403" + "proofAuthorizingKey": "dd2c457f45e3131857b78dbf882f21d5f825f81362f7211860f565ce9ada9d05" }, "head": { "hash": { @@ -6003,13 +4741,13 @@ { "value": { "version": 4, - "id": "2922b6cf-3662-4059-bf2e-0176957f82fa", + "id": "a4e9c220-3d1c-4e4c-9d24-4424ee2d47bd", "name": "b", - "spendingKey": "03ef7aa84b14d78820cdfae07c7e46a5974b9b42f98d3ac1dd56d9cc77b625b3", - "viewKey": "5783005de68cf30674842c1348c4a770552bcd7f4ccdc95f90d45a35e9e9f61cdc5e03670f8ad85e438a9634b17ceacd8dbf8ba1bb062d6a2df943a556137114", - "incomingViewKey": "d4779702213d80c09cf32fcf339675fd42f7df41c5881aee93fb05a858c0db01", - "outgoingViewKey": "aab99e3522c644aa7a35fd99242b012530f7c92e5f7d09883345a3617711f332", - "publicAddress": "9b88b5a458a7f981f54b9ffdda6532677519dd0c71f71559207e4df60b5f2e6e", + "spendingKey": "9a915605737f1a94b9cc8db0d7fb3d7b4e6cd0c3ec25a2b92971d5d32677121a", + "viewKey": "944293a643ac12d0fd3f9528dab304777b10702243562f8532b890b4acd0a41fedb2e3045bf3385e577dc051807e559cb8ef243292101e473c07d0980edc6d72", + "incomingViewKey": "13de79e7e2ef97fea168a875f3f78d0b4e40bc17757c46d673475b992b6dce01", + "outgoingViewKey": "539ea34dcb6523714f018c29b17dc0450fe1735f0acc7980e74038a1ac7f88b7", + "publicAddress": "5ec695f11f12ae8e29f508edfef57acf4a2f1ec4a157c803c9d6365ce119143b", "createdAt": { "hash": { "type": "Buffer", @@ -6018,7 +4756,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "4b7a55b3ca8d174f7858f62bd1704173147c2506e7e0110a43972abfb712bf0b" + "proofAuthorizingKey": "640914444bada025c574a7553cbad78e573d6ec2fae65c1df5321db909d1e609" }, "head": { "hash": { @@ -6034,15 +4772,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:2ywBgNNP8H0qbOMxslC54Alzx1npm8LN9PZkbmzxVms=" + "data": "base64:kD/0V2KfYuAAuyhjJ1rEUdjuFosJ5ZOXBGS6fa+MCms=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:iqNdPEjFbXj/df67x61DdErXQIUhWxLcDcpAzEaQXJY=" + "data": "base64:qn/FQnlKaVfstPrKsqMgT74EPCnldIDowffOxo8imuU=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538817506, + "timestamp": 1717538943989, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -6050,25 +4788,25 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAn+SgA5elA2Yw2XHJkGWcINS3NN3LYbM5gv208m6zZpWJndQl6wyYJK9WMOnOT8jdtkrmmbtcKLt9y3wwNeuh76dKefWQTYno9U7jBcwQa8WIBCD/P10fJo7pEEqoZLiutXHm6zKqDraXAZtmSpKFie0zzkQGy6eZMY5DEoJKTlUZT3HB9lhJ+Zl4nUdvAo+0tUJpE8j/+R5cZ/5AEzTgJcCgzbopLy2eNU1jkgUeO+6A3J60mFjtEcdaZ6P+HhvddNQGbnkICUhVGUB6J+bC4nMFrV4UIBYPcwSa9oS9appixHG/NgELn6EiVnwcmmq9PHr7JL2tdiLpM6/sdbyQnFeEeRQ8Gh6LaF1Wkck68SVzcpWQUBhAQZUs0PXXErxVVCQwjjNwk5mP1wBg3rqIYvHTG2HB6Ta1fugrloURYxAwFz683WK3CZCoJNbiFWQFPxoGkaTCA7qxrpiSc9s/f77t+lrrW5R5oJhMnvbjH4O56qM7uE9+7k1ZJSEV0qxZLU/BETrU/O2gBUdqSxLty60HLCIc5LnqtJSvUuGTMSGZXCTWigdHtH9SkqDU6g1l+wocnc3YYfN0DAWXDwjBD3ULmszz+n6bfFzxUB+us+A7WQ+VwD/6qklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwuN4KaJjKLCHOFw/cn2rhAF9bJCqHXgLuedmrK0xz401fRpsaz1WjPx7g7CWPUX5XgOSGU1ShMd2B4o3cdj7MBw==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAARrRd4fMOdXv6ERlKatGHMjzlPZZxMB2JQS/Er5qjq8GorCef/Bk0Dc5HW7+t5L4U8e/YW7+hV43jtiD/wqQIa590jbWOqPPvYzrWGKZXBguXnxyq0EqhayGREmDIApDdFNxWm/Pn/RvXiIbQ71FLAo+y2Zphnk7RYanKTOM/ohcTx1bgVF9L54fk72WX+1LvY2JT1jr59Pd0PjyveSrpjICvv7bw4WVKbEgrrOCBrc+rt93jsA8lJ5a3L8R2ykHOfWw/hfGcn6wycv/oNT9oFggWBhntgE4CpLpREmVuP0DSlzUa8Vv54f/thmfBT+ZcX1BLbwZNvFdqdlm9meldKt4JIrXWxYwowFrwYdJV5icZza1/39PU8OXLvY48ItE9Q6xuYmIUGQiJ677uTcIELMgB0cYKPN3BsCz3k9/kwumdBHlQPXfnIFQkjCrXnKTxXiEcL52qAjgJaniiCBlC9idGL9bijOYSPweGUa++/R3mYZbtibA6IsZxqatBw4NAwruXDsS1iEoMrwq3lA0uIyovbo3x8LPVflanA2F8SQpjxnpl+PNm3z3qwAtmNH8EfY0S9EN81cvPI5tucJglsOcuf2W6OLxSu+8eKZW2uvCHb2pkSFIlMElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwQwjNX1alENo2d91FWMLxrni6nUGPSHwxCzMmSvVpHSVN2PVwQEkrHpL1m9y7WwWDaALzaxqHs7+TkIYf0IeHCg==" } ] }, { "header": { "sequence": 3, - "previousBlockHash": "87F7E2DA3BDD6138E780C429171F7D03D4866EA7CA666787C71F372D73C0779F", + "previousBlockHash": "40B02466CAF9ED8415965338E5CE0EAD1E7D27C275E6CAD55BD573A04B1B9034", "noteCommitment": { "type": "Buffer", - "data": "base64:4fux5iPMDcHRKhXYTfbyUtiI+Ny9akWMFl14jDuDJ2o=" + "data": "base64:wxUR8U5uQ5+diof3bZLH1O8Q7n5E+T69JYjYZ7aaczA=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:7Kmz5HPGmfr0IqxHToKMovYX7ViGL9wgOdwrE03CEbw=" + "data": "base64:uiQHkSFVPaWVvtcrOnglAc8idhVSfYoagSvqphapeC8=" }, "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717538819526, + "timestamp": 1717538945379, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 5, "work": "0" @@ -6076,25 +4814,25 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAUc3/rmPJEkcUH6FoJ67mpmB+ho6FGEvAVVJuPdX0x1qkQmJAa4Maxdw5tgOEL1JyGpz0x156c3JBkgAC5/enoJ6/kl23433GDl3pmmuxpIGCKW2ve2/rb6DLdbxwPiV6+gFaYCX8W3DmAxYAhpIkbMCamT+StVD0Zm8WGg0n2N0Fn0OS3HTPa6gsae35IGo97X1RcegTeH5BRa4bQ5r/gdoiJAbL6JzFJ5UJVLEv4jGIMiMEayJZ1uuy8mBagJR7DofUrMu0j2lj3nnAt8T23dlXCJmHItNJPvgTVfInfri2FYE8xJjmf0VRKWBF6KdZ/QiLBwmS0zVMQCd+koNel4UsK3UCK1pFIPudJi/QXCEB7jW17mI6PRcpSlx/E3oR12JxX5idF3xLs1y6eVwQ+caSWrN95nVGZ6DCroidBu3fLNX74bWqD0o0Xb+v9/F1CFJTmBhXERInb4qB67xbupkrtzlEOqjuqN3bGHgq4AHRYO5R67Uj7Hq7YRcHZVOr7C80OS3q4FBwoyeDusfU0ZhYi6TjGLcOz93hmqV0OsrerKsK82vz/z+rExXKiAA6TAboPUd6BYv3xhATyAVXAxhhusOyE3LnKqNE8yF26hOwzw1XTnW9l0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw9V6ltxaB0huwsyo12VcVbxswvvdoj7gsEmAmlvOLTZWto3nLzV2Vd0ufrnwaYf65hVAAZqcidYDOQS0naptpBg==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAo8hSIw/8R+iPBJtDF4t23PlICjM8tzIy5+Mhmu1cAzWovsye+5OC8dBlnCWItgwZPRSsJBkOHjfVxc7DhXuGft+T1UsaLtOXizE/tjKkJNeIfYhREDfyl1hkVLuUgjXbVo0c2hvwfdoXxSBErwRt2bQ+7rXXSxlh7oTW/I1xJL8N15qz0PBLg+QTjsPp2L2dZALD80agZlXOjnflmxYX2bzgcqVd6M9lsRbUdnsuSnKUOT1kxw0I1cigm5IipCFlaUwNJteT6fan3M/MBD4YEViYg3C4bFdB6RjW37/AMxFNUcz4QKCPjJEhUH94EcWo+WegWsBNykALQNuOOoiVybTDdLhRK+VQ7/yoXv+qZIddNQYZlhSTNwP7toBxzy4rBBXwp43PqEh4jhQvgstcGWYo540HFVLFYiQ7rrje31abqqtU2RrGVtsQrxZazxqXwnrJsd8FTnOs6ImbGDP5PdNDE/OoAh5ozhWM5WzjOuummjzj93sx4G99naHuF8Ey+VvFPexFY3aYLzni2fW5/uXibLJI4y5wtR2lvtMj7mLfz2Tjq0CUAbArYtwbyJdVLPO3cOiJDgOcnFBc5rJ6poU4vEMPDFW4XMihGL+SnMPW7VEJ/JwZMElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYWRFBFNE4rl0YdYwffKKrmmNXZD9pTHOKfqya37ws1u6kD15uzqCrw+dxd++66U4o67ghgJujvNcYQHvUZkrBQ==" } ] }, { "header": { "sequence": 4, - "previousBlockHash": "64BC4CAA0864E98EB217AD9718387D5EA97B1631D1DE556AB219C70D2A9FA9CA", + "previousBlockHash": "9C5B62C46AE334039F95FA2D1283B13CCF490FDDE4AC28D78BFA62B7F90456A9", "noteCommitment": { "type": "Buffer", - "data": "base64:fEbtSjyxeX/k6mQSno4lRP8X0a7TxBi4ii1SJyQrBx0=" + "data": "base64:DL2vFwoSCgdRX3juXF6dXT9YxBSigN2wXN9V7ownxVk=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:Xm4OsaW1zo5hFTxslay3UKW7D9jgtEgN76jtg2Covyg=" + "data": "base64:1LvYS+S1Dn96TH3DwZo3mKvnZmFLPGErmtJL8Cp+dr8=" }, - "target": "9233318228143625020618577701423519925017621426082203201059080050516648", + "target": "9237817552710710459325694168790757901687427753068917234625768156097641", "randomness": "0", - "timestamp": 1717538828217, + "timestamp": 1717538960856, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 8, "work": "0" @@ -6102,26 +4840,26 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAA7/BGXjBaoQcXdcKt7BLmtO72HDn6vL3yrvfyo62QmNep440nAfH6zOxZ2f3sEbMvVH0zyBx3jLYxrjMudbcXiqi1M/Jf+EFHwNZ/d45YDGeNbClOqQRpAbd0FRAkTQtwMuk8WNI1DX38Yv6TzOYQoPROvNilfyesqxAHC1QPmyINd7wdXGOiqPX+Z6gBqLlmiywSvmSpsOZRmTN6YDkfO4EicLxhmITOWsOOsavp7faqENyNEX8yXxWCqfj4O45J8t53jaki6hmqaqT/utiYr1klY9Oafw0JIY78qgGVQZh45LK4deKtPd9SEutqP2HRh/uUF90bcpu/5z95XIpXtzPf5iKkoYrqKaQLfcOfuw3rdC2JGOtdoTWa4UvoS7tTFYe4lhSzh9gpwVVgYG4LoDywirWcwK/a0tHKKUeng9ooB3+Dtx+DV5rfMjjKzadzC8TJQ7gpCAgeVRj5B3deagCmdBoTBU5MebLI/5LzyJF8kADk25h/hlafHP98JMN8G22/mGltDrTqHmG0O0kP21coZlGt4qZ75S7cHSKclQg8jLQ+XO+ONhe86vxozEqg+XezEgNrNFm+cKyyhk6yGKtDMrWq6LPxsNI5TzMhW/44ycbym9yrsUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwh1LckMrSdyMKCQf4nrXWxGzTJhaoB2oCXGv8ckMUJ+DdUy1OuVHww5mbN9mErDs+oWj4R4FAeFVNs5ekXfsOAg==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAA/LHvmXP0C7WunnQq4s5Sb0sSsAv7OM9rwNhnVxL3jqWopCzCiW8R7M/1UrcIYS1l20OqB/HXuMbW9FJiyM7290ZKA46AX3X6M7688rXQ55+ZiM2ODDygDexvMukbR3Ta0nLxUay/HDdkYkiDYZ0xKwhPKo1KZWjd/svnQ6w3gMEBBHZe/plDym+VJTAWJ84+/L3/cg9uClWIPob01zqm2GESETQzjeviPgtndP+9Gf+o2MAH+3ne0IyfPEqP5s1mVggJaNOwacMIEKMG4kY6GOkKAzQAT/ThnjG3zMMke46cxA91SjrRsyXCXuo2bjvJpoE45vypUdONEI6XRiNUJ30g70leY40A0KefYfWbBEV32qMtHFTxPRpxAV5BpI5lIBR1nOpgnKe2XZe/fsGquEQoi6vRDw1EIsqpV+RzqKUnz3PXr7SDBs+Np/ukf9bKV6iL8nn3/SuPAJqZMBX/6DigDo9h4elsBfnT50X+yDAti0FzJlGUXrnghwvtVomqgAFsUB1LRA7ooSOy1ZHUumA3ZtOkfYIUK3nNfEfo2dlrie4yxWFnjPdajSPGyE7pZiExygSIkRYkPFV/RbAT4wp5gUJAS7xwExEL6fjOkA7X8hIAh6xDiklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwFrKErzF+1B8ZY9ppvwcve/QJqV5qA1G0JvfTdbjTQdwBFbJ1b1G8nhrJ8Ypo+uOFL1fvIldpZwQnEDt1GNYyCw==" }, { "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAN1aMPhqNBjLikuXaTnrGJay+0RQWcuYK7vLqUlSq8YSNPYzN2q7SCaMZYA0o7TqT2CmMfKQ2h4NntZaKOyunnbwxSmWQsA+dRkPvGbOvl1Sw4HOa1ZbWSd8vEYepSc2uymGeUnTKRA6ixytamFERvJ8WhjxXIl2JWaMeaOU9qUYNUF5T1ma5v9oLLuHr/2bdyeHYOVwtw8DKJAPiXtlcs1Wn5/TrYrHBDkIzkW7/coOO5x70pOqXR8VzYxol7s7yfxl296esMiCn/lQ7F30mCljR0U68SIlTYghU7FgRWhICv8DvlDiPbPH0c/hdvp7c13DxxcThoz2BqpFfCKPqKOH7seYjzA3B0SoV2E328lLYiPjcvWpFjBZdeIw7gydqBQAAANXKct7eOCT9pozgY6hZ3h955onOuWs8teQw5kKgeZR9V4NmJZlYR5/Pp3uhCkDa/fMpWKwPddJv6AfCmjeZM5sJTxqAcnNrSpsTEEiKgh/OUy+ZWHcrjOAriivy7RKkCIWckvomu1lugL+KYyxGk65YhlnuxL3JpRbZMOuB98Bg35dJTxC/voIABDGvPbyrS7CyLiy/ofQ1yF5wvy+yc1kSBMVJyagnFK7KHILJYFTTfjJuZEOT5fMdZA9F74PRvRDEu3wXIzSs5OKhG/VK3+iLWuJVAD5czM/+kl4o0g4952mPUwydWRljCF17WkY6D5LrVowNQOS5hZ9ganagZO+kFWQlQ/fQI3tY4ENUA+O2lowSvDnH+JK6d2rqN34EIkLfOi4DWleUZ/1sgwDhx2ZKbZMjlbl5t5FYFIEV4MRSTr9WR1HVLQ28k3a3uH4JN/aapvCD+2itPxlEotV9xi7Io7Q1qG8x8FjHQ4NrkV9IjSnGkEoXYRRoO4mgPweShFYtPm07aF2SmkNX/SoqVSFNfTrQ8bWhxQixtHsNA/4hfxvCuiyWD110P0yOdn8+3Y8o+d/yIi58MX1NcU3Wsfrpla/nL0Uq7xVv81qt5+Hb9hY+L50QmuQPTLtPjGUKt0NRgiTtoNeQNdAgavriJoesNBk3Cl6EpqP7HeQIf9ZxY2gPYtJqWck+dkrqjg8xyRT8gTqLawsR6wJtd21lKF8gMaz0tByqwLIGPUEayzufec17RE6L3FK52YPbdAc/Yav2JSEFtHOv31G9um+v0sJDPJWR2XMNNVvCPl/oOWGT+a7YRe8iwBiqMklJtIcnxRThFTLCmzyzuOT1rno+0Wm5Vka4mbKebYT6y7Pn7IF+J5BfSIteIt+WHi8xQ+Y+/3hLYgmeCIifW6THPBj81/u4Dvg3Zo8Sna10bzOGJUvrhIwdjHrJH9AWCYubXgMr6ha50e5+3e5G6FJs+6Yb53ElZ6EQk2DCeU7t9AgQovhaNcpkJEEiJQGCtRaiftjtYbCJPu8/TZygIxMVoVDOlP9rv5rEsbm44hg5RI1OaBioHeK/9EfaBHuFkNK2Fx0OuvgaKL9b/LBbRVqdlkm1feJDFbO059Hil3orb+PU7TJQ0Fs37c10uRcWhpARRn0KKAsox8QP0acpbbUx/DvQl/iIFR8u+/De0TkZiDWg/EElwthJST6Cbwvclj4yQVjk3ddoGiWDtEHTmPUioyfVOfCcuvKVU6fEKpmxHcn1vlimrJAit1pIiVMkXNUZ0pjFOcv64hWSwL1PIfYyEWe/yJ9xeRODjMbPjeXYgjhiAe14Vcbf9g+C7/gBpu3RYRx5iYauB2VWqmtuHfewX/4K4QkzBdvGgZqz23r7q81Vj5RlPpQtQSfD0eaO4Dqm2f5whiER6OtfRXnlmGE31luYCWrpHJxQaWdfJYwnN2LzR24RDsH5yzLZ9X/Ui6hO2ZCqd0NfnH1LpOxHMJr9P/7iF3/M7PkOuDY4db69bFEqhQkkvOuMKGf2HATeMYHC5452kafY2yzsRSc6pYzes1lGoU/8/5Wm9r0qLOurj+hND446wcyKWicieAVV9Ps6GNchAQ==" + "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAb/X0X/HI7xUdgYl1jiGPiR4t7HdilFNJ1ImbNhdb+3GqmA0ZOezL4zI07AqSDpfDCVxGFUWVZtI71GNgXkYgffijxVP0HB5WCJY00I8pr/yILWkGTmsGx2ba+nCETyGQzTynYOHQDPSKCjP6WeVNQYBGH1NS31VmfQabfM/BBmESEMsMRGeMnMVkVEfpIJc+RC6pk2X9V7/8sR2He1f0eaVP3nrumv3RCZ06HlWv/qqk4jNc2bYxPuX1QbEcN1+cp2jpwjaZVj8Mz7bCq0vuFVmh4MPNgIFzBkXALZDfr+8r42ClnixuJnRnycBDTR+Rxk+30ixo2f8H75jO/yZKpsMVEfFObkOfnYqH922Sx9TvEO5+RPk+vSWI2Ge2mnMwBQAAAFxR7JF5Oo2tKN4jwUixToZndxikLOQEZNNxXB01L5mCAyNgCmShjCt/7QWttLAwWnyb+r04vzOlok1r2bQFKN6t87sPBvP28FFHv7i101RFKYReRYTof/CrTgpQPKqMCIbjqqtSMFYv/Ecp2P5bmN2Xir/5Y4rrJose9rF0VqDgvoa9zqn3Ei9lkAcyHTTUa6ak0XMP1ilQ6wdnR7XMU8M24DeuVY/hqzlNIiBoVciLwQXPNZy2hm+rRneLDNRYvhnmaBEuzloZ5uM0yQqK+Luo0N28+Q8bzHEpz3j8VAiFbDRgeZw/LL1L+mdgW0OxK45s8LmwjtsOAYLmwD8MIlPU3jR75tgMMs+UBhyft2OBW2hEYkt0+wm3TgS9z248nl10eHEuSCtU3TKhbLl5oyPOIZcHDgUTf/GtX/Df4Xo19Xs54U6ordx9+ScZk8wnCibCyUCbSb1YC2nYu3yDpXMqEGoGtcFJOefQl0gBMAm68YjAsB6yy/ibxv62sqYIJs/2LHc17sb2WKxTTkMZxppQrtCadW4FqnS+hujPOc+i9gABoKUyqZCs1IMumY7g3T9qPbq3tnY8QfgB382mP5PRJoLmrX4lPWXKUVTqrY1RsteaqnYCKF11mHY1rBRxpcdZJgqntDNreaapPwDts/U5DDir1JXObJXJOVDDOaUajllDNz8BXkikmV27kYa6i6vEywcwTRIXnx1b/Hk7xr2tV27dHIJV5zsZ3zrVlHfQEii7xxQIm1sN6+O97hiyqQtMIZWVeuLtlO2t7b24p0x5ADrKJ3JSvzcE3qNszUTf3k9+6fIAcKyoSp+Epd3KpmLa2t00adRUyYWMew0z3w2SFot8+TkgZiCBxjderq9bcwqiSnIGVeSL6zVh1AVgUX3zwdw1CbuRjTYwc9Q65Ftz9ew13CEUWqS+7PTW5tl26Gje0oN8BGgMrFstW2tF3p2yHOeik9YX/UY+4WI1tuk9E5fdHezUQbgZKnqt0Qw1lQiCxbMmLS+GBZwxgR1fLKo0B2o0zbWA9+kVBkBLHQMykP6hhqWMgFf+s05kAYslZlBaKNcYO5OG0zDnpunoTSTcCdtLVizIW3E074MnKvPuKMauuW9SBS39FTJ850PyFCeRz9ExEeModGNZFtFjyQDu+DhhfO8UNAKyyn00CfhH4YVprDWn3fCfz6bj1KOqjSnuWXS285pppECdE+nVE2V7LVDTexWYJyyjmVlRY0m0NLTNrjLRWxzT0loESC45zs/M66e8EdWM50zduL1aAeQreNpaWwBGg8rUwQuuWomwyAcVo2+GgTqRC3kFCtQSVaHB801WHcguech1wTaEN1hl9NjMCEOWYyqaJEwUoNlz+lhrlrTfb0QWCXz3d+X3Ci7R2xhyNv6Dv7+HIHKLASNOMkksbpyTUdkYaXeK2B+vs3shVhFX6WnM8+0jZq0ZT91XBybLPUfZR1LpwlYHaBLvJwvZGZ1TGlItQkJ4aHmcgtOQmp7G9p/vV0RQlTIlO96ioiSHoui8Qv3Qp7uSltg9Ip5GCu9WvlYzhdTeShCnlrWhrtrYO0eJCtGN1SG8o6v0UBK78mkVRlyedx9LBw==" } ] } ], - "Wallet disconnectBlock should update the account head hash to the previous block": [ + "Wallet getTransactionType should return send type for mint transactions": [ { "value": { "version": 4, - "id": "23a13952-9ca1-4fc7-8b8b-ca7d54528a7f", + "id": "1752aafb-063a-407e-9a89-fa6cf21251df", "name": "a", - "spendingKey": "43d0225b1300276945a95e014300ec96efbce8ad4350945138f974640dbeb8ae", - "viewKey": "a2e4d5ebcc416605a649cc105bbfbc1744674b87fe96ada78e0c25126ca7d3ec1c13945530ff3c86b9df7464db04f3c17036926053a127e94d0375de2c4ceb55", - "incomingViewKey": "85be2a194babc67ca480a7fad7fee31de1e22b1bf6a42c180b7957f804bb3b03", - "outgoingViewKey": "37dc14d6db97de98a5cb417b4878abee0da710aa9852b78509457db265a7bd9d", - "publicAddress": "a04b7f87ed8da22822fd6e12d8e4f1ff026e075d81170fa389619687fc09bba3", + "spendingKey": "3ddf6ef059038d3e00eb9c054356b73243d47b655afb53de6780406ed467b70b", + "viewKey": "93cb0e9ef0adc763018cc7cccdb287024460bd9de22778d649bc021f436275a5bb98700cc9e01b463d2eb1c6fc73468874076e37224ae57fff95d51dee4566c9", + "incomingViewKey": "e1d116d102f3832dee495bf0b8e22e9ac241e394c839f4a3cc6c17b70a1ec701", + "outgoingViewKey": "c481bc21735be6f39e11cdcafba614db85f4183153b8b539641ea0830fd3c11d", + "publicAddress": "f0fbe34510ec326ba09245a4d54837dad8b32031a2d9fcad7cc79e66f3effd17", "createdAt": { "hash": { "type": "Buffer", @@ -6130,7 +4868,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "82ad3b2281fed4dc1e6688d397fb8c821cd31958addf28d49bc8759fecaf4e00" + "proofAuthorizingKey": "ae8e40d882b798ec0ad76c8cd52bf7930efe746e038b86607520c2016388910c" }, "head": { "hash": { @@ -6140,16 +4878,78 @@ "sequence": 1 } }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:onCdNmggF/2oGVp4f/p8oknj0nj8lc/SLDHFGX/YWUI=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:52ERR+LGjawjWBROzSmoSEo9uPzsNdDEI8sI9O63sJo=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1717538965971, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAArn5L3SkJFV6D2iI0M6BIYRDQBFYZmqWKrv4hi1j/W6SXq9VIai5CjFHYLIaXlJAEhNtquTP6IgL9896gqhnPqbdhbd6HycGg6y+OqPoNQwOE3VG/Mv4hPqMWnmzBsGZKnWyg2+IeIOvoKSewBuEMklUN5o0uFz0XRXFI0wxXuVkMSJDOn/Nv5yedmJBfu/IqEux6Brr+a4GnLZR6RprhtIxRnS0srk51wjCjdqN8peeQ+WiWTwgJa/MzU4l3v0OCN0rWHZl4b2Tt1dmb0RNpzW9UB+YFe1ATOl1OzosOKebiivMk0TUKtpOntjzGXqLA7gH4pdN1FepmKPily3yl5IegfpR7B+tQnjCzPXWOqSs5Wcjm5HcwceztZYly9yomBmTrGZ/peNaBim5F0ckO17BTQjQhS/8AZIo4SYmP8VuLirAmnRo1oy8hgGynYBzbTbJDPj3HEsT1xQRzB5+ed+28CWVuwfpA9tC9tpFpSWFe7HX4cvIQZBkojzPG/1Py+dODzRnQLv7VrshbnpcwAO2UPr2AHLG8Fwc7zIAZ24F6eNHN65aHb+dUhvHXjTP4iDlHg6yNXllku3FKEvc8kVQaB2LhPHW3lwKnNBYFQOlWMcIrwYuODUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcjJfwd0I9efczfg20oav1ibON8LckBbQkoXToi18vhnsAyplf0g+wdX/SK2N5eEXIyK7pJ3qeVlt7H+vHCijBg==" + } + ] + }, + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+MdUUPjPr8bYZCJtC3J2W4iA938sRS3Qoe2BZJzBrgWKvokrOsO0O/hDpcz5SvYv1bSNzwldDXrJoRIzVMB+cYyX6fkN0Cx/oAZlukvFI/muwiPA75GDk8tHuYvEoDvTw7WRve/ltH+QC/NJpquGnsv3svnkZj7qar4pEEQbNpAZovB/TUJD7Unzoae4escwbKsFkdh67iuegyxt26gIe8IHn2kcvn3fS/bsQO55NcSXC7CauLu2F3gmjLbgq9KaOpbbwifsHGXM+zW/UWzL/V8HL9FAISt+4NxP9wxwvxa//SRdJ7MguUEr8JXVSQxK5Y1xL+gipkqwzwqOshkcNIGpoX4013FSoIAbcnzrBqHWfhGihtHLeDyC9co8Lgpo/kVYSvJnqgtopdigb+8VY254wEvt8QA3vByw115LBm1x5yRh3mAxFa1aSjedz0X58ErncHLAoXqOXZbsnxLACEtjkb6dulKxdvzl0MGQBZvwYDCnlrq7t5M1NJlnMNWbCm+zmPCTEq4lLPRKMj5yoO0p0i1vDZqSpoatJbFHE6xMFcuyGC0DGpNHRCoFlfh2TNXLkaZl2OKsob4EjsS+vqnT5yCnIGbe8PfBVWwN5zwePJrYYJspTfBUnQ08ui+eGoprZlSX6jYdY7h/lO7TXalWQ0iqeZGcNlwLwF/wlnSTnyuGOOuzAxuSfR+TbvyIunSmOlLl9+2rnJ6f7vRly0RpUJWc0UbMp5ReHEgT6FrQRkJy2nAEWEKipTKuJGYKzt8RxpaobwfYK2A+9kJYds/lbtE9twVmj64i/CQxWL+zkurINVrYaSMbMEckGH47cH8xEzMFdc0cb++2gIvRLrJd7nRuHbO+ELzsz48eW2lGZL5PTuLqZ5u0sxzI7HOs0Tscv/xS4t8BAd8djZffsitKJDqPKSTpmRwkpkCBtxymMrFqTXujAkrxQ4vWUK1jlES6PNB5dntroZqwWXBNALcXRL7lNOrP8PvjRRDsMmugkkWk1Ug32tizIDGi2fytfMeeZvPv/RdmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAKVllL5fZK+LL8CsKp+KUlDcVZSF7SgJeIKXj6e+BAY+rWp+ecYfUBZLX7ZrP+JQXsA1JfRbjB9cPp1n6ltc+Ag+smvti84jb2uZThx+QUg3rENLxJc8EeuLnnMV6fA3pt4YSEZ1gBEbth0piU1z9vPdnpyi8jS5HrWwtBHLKngF" + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "87E305098235CD4ED1E59AAB0E750C25DE65C2808B5142C0DBF463975747C6DF", + "noteCommitment": { + "type": "Buffer", + "data": "base64:xOp7eiljMucd2lTHf590CbEP6FhPcEsBkx8RuinEsxI=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:Nixjzd7o7fnd1LWmI1xLyZegfs7QSYMTwiLyggwrzgk=" + }, + "target": "9260366780148527510972123832573278885902566341756516011968728852484845", + "randomness": "0", + "timestamp": 1717538973214, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAKF84UDt0oViCmGUPnejc+ywBy30UoeOGW2bUrCMI34Cg9rR2FynPxlwGN+QgmBxjnEHIDdLHLIvswKI7uV9lA3kCQNrhje05X1q3cG1/zHWNXqOUYWwN0igYCCUH3eWBNR9pv2jTtH5JTGomyYvQEbqQkTezxQfYbxTCUXFZ39QWxxhwHrM7pg4krwcwucTl6DBpTnekawRDmg41eUZ4qIAI0vwjkzvIWiebFupRaICATpH1koxEMAwzbUKeNntYeWCNFqmeX656N73yeAMfq5uOYkv9Drf1PWRTG2t+bud0UbN2p4H7MYha9Ih4bPgAncyt+JpIuo/aiOsjzwE7kRw5eJE4Yz6IKVRG8UjyoMrpiqJPAOcMeWn6h7WjaPhRz6i6+dnYjaojCXuQj5xmzg2ppthvdABgqVYLJ2eeMsICvfLZ85b1hyYjcaLTbKiBzq3ccxEHQrBMNm2Tm2feN/898gKKGCqDD4NaRzVozuEm4U3Muyl5yJG3ErHYdPXYCS550Joxk3PQl6S7w94yAZKMMUXTBatCVcc5sK/7/LU1gmjBmkjp+8ZKdTnQR5In8HRh8HygMxNhPA0tKSO2kAYGG8EdvMsdzeZKndldn7pVbhLGwuPQ/klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwqcoKO324naEHo7H3CsJYViRrjbMdiIaOSYTVU7gVuHC6DBbVm5CuAu1beM7a+33QhqICVQS6w4uRSaj+mJrNCQ==" + }, + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+MdUUPjPr8bYZCJtC3J2W4iA938sRS3Qoe2BZJzBrgWKvokrOsO0O/hDpcz5SvYv1bSNzwldDXrJoRIzVMB+cYyX6fkN0Cx/oAZlukvFI/muwiPA75GDk8tHuYvEoDvTw7WRve/ltH+QC/NJpquGnsv3svnkZj7qar4pEEQbNpAZovB/TUJD7Unzoae4escwbKsFkdh67iuegyxt26gIe8IHn2kcvn3fS/bsQO55NcSXC7CauLu2F3gmjLbgq9KaOpbbwifsHGXM+zW/UWzL/V8HL9FAISt+4NxP9wxwvxa//SRdJ7MguUEr8JXVSQxK5Y1xL+gipkqwzwqOshkcNIGpoX4013FSoIAbcnzrBqHWfhGihtHLeDyC9co8Lgpo/kVYSvJnqgtopdigb+8VY254wEvt8QA3vByw115LBm1x5yRh3mAxFa1aSjedz0X58ErncHLAoXqOXZbsnxLACEtjkb6dulKxdvzl0MGQBZvwYDCnlrq7t5M1NJlnMNWbCm+zmPCTEq4lLPRKMj5yoO0p0i1vDZqSpoatJbFHE6xMFcuyGC0DGpNHRCoFlfh2TNXLkaZl2OKsob4EjsS+vqnT5yCnIGbe8PfBVWwN5zwePJrYYJspTfBUnQ08ui+eGoprZlSX6jYdY7h/lO7TXalWQ0iqeZGcNlwLwF/wlnSTnyuGOOuzAxuSfR+TbvyIunSmOlLl9+2rnJ6f7vRly0RpUJWc0UbMp5ReHEgT6FrQRkJy2nAEWEKipTKuJGYKzt8RxpaobwfYK2A+9kJYds/lbtE9twVmj64i/CQxWL+zkurINVrYaSMbMEckGH47cH8xEzMFdc0cb++2gIvRLrJd7nRuHbO+ELzsz48eW2lGZL5PTuLqZ5u0sxzI7HOs0Tscv/xS4t8BAd8djZffsitKJDqPKSTpmRwkpkCBtxymMrFqTXujAkrxQ4vWUK1jlES6PNB5dntroZqwWXBNALcXRL7lNOrP8PvjRRDsMmugkkWk1Ug32tizIDGi2fytfMeeZvPv/RdmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAKVllL5fZK+LL8CsKp+KUlDcVZSF7SgJeIKXj6e+BAY+rWp+ecYfUBZLX7ZrP+JQXsA1JfRbjB9cPp1n6ltc+Ag+smvti84jb2uZThx+QUg3rENLxJc8EeuLnnMV6fA3pt4YSEZ1gBEbth0piU1z9vPdnpyi8jS5HrWwtBHLKngF" + } + ] + } + ], + "Wallet getTransactionType should return send type for burn transactions": [ { "value": { "version": 4, - "id": "3db24702-4c00-45d7-8cf6-d3a39866dc8f", - "name": "b", - "spendingKey": "9fd21200e06e7eb043ab19cfb0baf610ce78a34cac2459a309bf83c235c6bb9f", - "viewKey": "af25a238a5b7571f4a04c68cbf0a3d2b439140bceb4089037e6002dfaa90e50b5c562f0c23f1293933808a008239d4804d7accb73bc9c949b0c3992b82307632", - "incomingViewKey": "24375314e92a44b3ffee23a1ede3db99417d78ed573557002190bcc6168a3604", - "outgoingViewKey": "0170cde824d36c7b73510632c2eceaead5abdcc3cfe0f3dea276c1fcd2784f4a", - "publicAddress": "4df7683042db7b5452447fec67f8705fb987eac27dbc7a90b3952631fc9eef14", + "id": "e70d60b0-ed2c-4ecd-8733-6868e06a1f07", + "name": "a", + "spendingKey": "1e0f64aa9f3399ff327fcee2f958d2878b22c330976591ce22260ed49b7a6ae2", + "viewKey": "f24aa2d07299bd40c42bde10abbac5d2eb8a610ed05e69cb09e058427deb3aad28024d98a0418fe395b0923a0063926ec3674da1b2d70a08d0d6c85a332c6265", + "incomingViewKey": "f9203e5893973e234d850b65573003ba04f89c231195d0598a4198c446d89102", + "outgoingViewKey": "641d47cd60fb9991e6c62561f53f3b557f04a285ae6924cc5914718e9fbbe138", + "publicAddress": "cec7be662b6bac78af667994b490f26857d18dca1e728488ffe7de0b01c4404e", "createdAt": { "hash": { "type": "Buffer", @@ -6158,7 +4958,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "6f056d911f82921ee5b80b302cfb83be49c6a6d9f14ef3f0bd514733161b9d06" + "proofAuthorizingKey": "23b064a3b8d0933b948996bf738832e9f7c65e859c7112dd9b3ab9309db3d507" }, "head": { "hash": { @@ -6174,15 +4974,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:XEQ3Bvjk6XhOZA3wHi2/0JYKEr5N4Q4zuCPfdqkoDGM=" + "data": "base64:aS7ASLGMZiv7hpmqlHeNB5FRMyV91rVJBc1tPoKm1C8=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:qJLnZKHMkfT5JWiHaS0n2oDYVnQQ8oWHiDAv8Kbp8JM=" + "data": "base64:AuB8iwuUK4wG0Mq67TGU6TTpY4xgsEAbVKgn0qS0tI4=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538832024, + "timestamp": 1717538977678, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -6190,51 +4990,63 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAHq/ooVgaEf52Ok/jN7FT4CBAAIoUWTyCp4GX7xpxAEWoLwVq85RgQ1GqhEmlBrZyP8WijWJCNdyRrveFKm9RnK3NkLjqGS7r8T3zvbVH6tqHGM0N1kd20pWEGX1fROAQqw+FPYnnSOPZUotHirCtHcEvwedqCErrhLaTs7LG9r8DUdqPhLpbem3nRaR6xG/F8xpNAvRLe65RL4iMR73saKVMsQcCLGlKC8KSGDFdzpGy3Hp7ZwDGyq/19koEvI/d+eOlp5jEiK7NEpD+IRrbuUTTLsgeaipz4kbZBDSk5kY1/nEFAyo4HQ82UOVt5G1nogvxHsePf+36qI3u4MPpRzs0loinoq4UcJJH8HOpxDIIhmxY/t3n/2Dkezkkyw08UlSx54/WGNoiDWmAySIdc/wNb9qbUXV43oj9yKhinCLaLXvXY+hfwk+lGSwViwqPYZkUY8O3BkYDJ+Y0vhbRWVbNRfVZghtuGDVwDynBwV10Uc3f2KBuZcVnf2Ctalnmz/fdCXVzxz3OzHH/RpLyfD/jyo9HDD/DK4/cdAuDcXfMXiUlmwaMQKyiQC3jXxCuecB9IJa06sS5hdMksBWR++KBY/f/Ht4IT0mmumuNX9m1xlH+t6CfBklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAweBrhDoyFhZhF9bbrEiyy+RQt3ft23/UUjRvKzv774N2HP4kI3CDV1p1c35pJMV2VNRmA6UFeacXu7uzbK7hFAQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAT4+USY6FnToSV7ZjcHzWvUT9p6fvxmTd0dFshKKHhaOwV6UxnRaFMxwRGAiRQmkQs/crpIVhpvceXbUV0C/YbCWZr2K+T0GRnnxGzHWROC+y3YMorg10M5SUK57UFTD9z0pZhMJzJTqx4Wt/JLwXgwbEzamfBEBSQNyYM4ZX+NoZh/7ySogA208M4saEhys9gwb6DRcPdIpXozNsnnGN7miACrdJcyi/oF9akZyooCaRsGJCV4GDZ00lnor4AESThyoGb2xHW4YjCNfqEbdANtuECOKFGMc5B2SHb4h3m1gmH0T2wBHj9ToomU27LHKtGLe5XdEU1ibsfrwgFin0P7R7AJRrXhcw2xmxgiy3p1DpsCJyzEMSM2ApROZmxyYEkSAEnZ4GUvasMfxQh06GBZRVgrwDcRzod8HAcaJaFNExqNMayN+hVYOz64arKT1yAMGyKsdrhUtFz5HFVv0EVxz/tjwZDulRKp3TPwKCVw/9Z8U8etc7CNnBNUM/AAfuLbeXCKyrAga7Wgc613aUtt2cm91yRg4zREMCETkzgYPVqS0K2iXmTqha/GRiOC2pCYlfetpSemH4eOE7ql1eA7XCulNxv2/rFeTznc1aElOagai+E7Eyzklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwmv67lpCUh67l1lWOSSZZrAwIbOxlwXQx2mUV/wjgZkqaS6b1DxMVg4lRdMz1336XflHtqPG5UhzuZdIy3XQRDg==" } ] }, + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE77QpulZtkpkkf24CYcBP2qUgX+qSRIB+djV1VVfhzejHnuJ4vnj4QJahrQ35gb1z+6a/BaGG3zTTlp7ZrOk1ieWdwIDTSZoBDMLtRkTi2q0QwhnZmIUJ+i9mqVLUh2MYvsqnmNs9ClsqeB/fYPnwP9ZP3w/KMCrx3juijlfWjQNcsLqn+SMA3Q5ApFY+k0ZIX4hyLHWbOI4VCOb3Jei8rlQgo77HmjLpFOnmcLLSYyp+RsVm8BzJ4/3I2GADRJL63S7AWdej9mHqCRgJSnLxqEZLdTwyqHpyqsmfxKBHOKBnOW9Qn4pUWDHWtSGi6fPEyvdwSBWTMxalBkwujTRaB7hArdVpXzGZwbLKvCaRqPSjpSTK8W8ggd7u6WVbsZb9JWOz3+ShA1lZh4xsPsv2P1uqulzIzNoQglCPj2wnDBzMG4FFgvaUr8pP/XPpeSKnSbS8PmvQpAcifZZK5l4YZaBOiucPgmywFCpIwqgjB4i8a8UYGjW+tA1LGVmVGnhy7e/muL/xlK6SEKDtgF068Fpey7+rCsXI1m4wIxVw4tT/Y7aWa8C+jhqxHGOObv1hp13/71EQE/oE2WOE4cqcVlzTP7vUl5aW2IMT1k4yRVqNy6mNG7YgTHpGB2YC1znaYP92M0BJkhnyJG85qrgClhZgC5GzlT8P0umRD9IbgIyIajGL1kxZrHHtIImqywLFuc9CgVTlmBkJbO9RtJtifYuqITaEJ4bj6e0M7/fX+N786SrzdkjWWLWqsGfRaoszUx5mSYl1h0UJFY4B8sNq6U84A5qBtgIjnZtF6sKLFPky9Vtc7iY0ZXLxdo8NQhf3kyUlKBuZoq4Mqg/0Xuj21EFw4Lug2X6EXBiIeJplV/HCe9fxfq59ypcyA0H60+pVtUYuQ/amJ1XdcpYC1a5AAUbdGPpY4d2s3i2nTQEOVEON/RapXwSY8exLECBRuHHelTtBwDlm7sjb8kIfHQHnWLqtqcnrwNOzse+ZitrrHivZnmUtJDyaFfRjcoecoSI/+feCwHEQE5mYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsKAAAAAAAAAILb/O4cVis/DloIJXLK6zg3y2uKNGph8FYoizUSjKzfb6nKQ7gufjpDqROnU3F6q/rbkGbK7VKWMESV44HVDAdEabfmg+yZgHyrPqcXoXgr8Zw2LAjTWNg3brj9Kuwk7eq1xsSQL2MI0eS4HTCls3v4D8X2HV2Flngb0/vlWWIC" + }, { "header": { "sequence": 3, - "previousBlockHash": "7F840778C2EE7BBCEE58345E540CCA24BA65274887151D50D7C51AE1BC1AD330", + "previousBlockHash": "216409CB9937C70C611CC4FA9BFA37518E3AA47236B7EBC514C71E78A39926F2", "noteCommitment": { "type": "Buffer", - "data": "base64:VLevhByK2cSaVFYaNWBo/yiiAz+38SIQFC4OTVQUvgg=" + "data": "base64:mK95bK5rOxJgUHgQ/d1RvV/JLGkdNqXvwt+wt41hjGM=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:weNpITr/hPtmw7dvQGSCoIp5dTXHrQ9rd01PbDsmNE0=" + "data": "base64:2CY+t+au4Gl01WHD7KhEkKoAYC+O+FVMLwXrwcu7xLk=" }, "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717538833953, + "timestamp": 1717538981685, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, + "noteSize": 6, "work": "0" }, "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAiBjSTrwG11TFrZKrBMkbozpK6BUV4ZV2a2L/D3xF9eiSTMp7Oydru8f4SdPcO3hT9r/zF73HAnFRouEIztu6QMJfHv5yBeKP+UQ9VlcxJcKPgKxjefh9oHxa4zf536/QIBwdYI13JxQdou0HRYTH4HwBdBjXeF3dOAO2Ah39JnoJnFxWUbpyyICeyHHoxREtoi6Q/RQgJ+qx+tXKkYvUX08cJYr8ymRMzKk0SoB4qZesp4PxHlhbDsYBtHraSFd2unPRQnNzh1x9a/C8tJPYE/J85OuZbDSJeTGzJlgRkPV+S/6N1SOZnb1kJdI7r2L03H9Jd3dd6p5EZY3jBvNpanyP8t50E/daoVkBWwzauTFq/N43PSMEAYYYtU72MTMwxHq4H2srfd45R0v36wKg0k25Xx77jsL/ZqGQb2Q5028+60FTnH3VdLQ6jn5iUqga2dxB6qB1jxJyXPsQLBI137TPB+hPrTBA4pUD2QQhRMbZZ05VbPEs9bT9vE3Ef+pCbxU5WXl4yavl7wPr0tH2bDskCrtA/YbDBWZqgIMolpXzgvw/QOc9xzhCToN7X+8eMQb9t9zk8Z3nD6C0OOIXxX++4mniCbJAzBml1hY/vedIuFx20s/COklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwz5bdLkzzcHD32CydUnSmIf07YTCqFmWgAdTrY4ywvL8uGIGp8qIg2pRYcChhep8AMh/dz7fvblEfwQ3arb8MCA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAwIl34DAsfiNW+UPDnL2HSTpcTliMyVpHxLy8DYdIkdumwfJ25zQsU3N/1UaavA7hK7IhK8axVCZN/3kgNixHiHw9E1r1qiyRaegyOPP+RJO1R2IcLhaNqzQwsNsKoa4qq6j9qDzscfyuNO6D1EJ/ILY2kNEFjqWKwd+B2I0i3fkRBJVCweBA/ctV3KJh8E6qgysP5lNwaSvBO3jc8RE4/+JviszvLXTyQArXDUjzVu2I3wkguvUbnkbO9HY+MdKM0fRwTRUxjSbYSaoDWrJ88SychritYLhoLlXf1a19kt87HsCyUmYvHXIt2/76PY3O48X3JBmxlsXOUVmVGerwDkcEsYeFKwejca+GPz/6TtcbDy0kGwNdnNU/3EHotKpAL1UGFmBV8Zab71cXH/Pxtnwsx6swtrvuhsbK+B5JZCm7yrEeXnrgL1uX09nqYi2ktmWGJmAhE7pio837v0DKCW2u1V2pisUTAiHOxfngkQ3PFr+YCJs9Ah1jq8pO5nl2vHUQwzTXl2wQQ51z//nYaipev4kXNrbF62tNDi8eg8FVbgpEAk8Uu3wMSWLSA5/PyN/T7kkTPJSxDaFhKBZx0f/apTC++RK7W5ngLfNNcTGZ03XSHfyX9klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwA96BvNjRm4Z61DvxXmKG+5jFYELBdvqecqwWOOZpCWBWIaHcE85CVEEgJSstW7xB2DRMqh78BRlmzLxpRxYwAw==" + }, + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE77QpulZtkpkkf24CYcBP2qUgX+qSRIB+djV1VVfhzejHnuJ4vnj4QJahrQ35gb1z+6a/BaGG3zTTlp7ZrOk1ieWdwIDTSZoBDMLtRkTi2q0QwhnZmIUJ+i9mqVLUh2MYvsqnmNs9ClsqeB/fYPnwP9ZP3w/KMCrx3juijlfWjQNcsLqn+SMA3Q5ApFY+k0ZIX4hyLHWbOI4VCOb3Jei8rlQgo77HmjLpFOnmcLLSYyp+RsVm8BzJ4/3I2GADRJL63S7AWdej9mHqCRgJSnLxqEZLdTwyqHpyqsmfxKBHOKBnOW9Qn4pUWDHWtSGi6fPEyvdwSBWTMxalBkwujTRaB7hArdVpXzGZwbLKvCaRqPSjpSTK8W8ggd7u6WVbsZb9JWOz3+ShA1lZh4xsPsv2P1uqulzIzNoQglCPj2wnDBzMG4FFgvaUr8pP/XPpeSKnSbS8PmvQpAcifZZK5l4YZaBOiucPgmywFCpIwqgjB4i8a8UYGjW+tA1LGVmVGnhy7e/muL/xlK6SEKDtgF068Fpey7+rCsXI1m4wIxVw4tT/Y7aWa8C+jhqxHGOObv1hp13/71EQE/oE2WOE4cqcVlzTP7vUl5aW2IMT1k4yRVqNy6mNG7YgTHpGB2YC1znaYP92M0BJkhnyJG85qrgClhZgC5GzlT8P0umRD9IbgIyIajGL1kxZrHHtIImqywLFuc9CgVTlmBkJbO9RtJtifYuqITaEJ4bj6e0M7/fX+N786SrzdkjWWLWqsGfRaoszUx5mSYl1h0UJFY4B8sNq6U84A5qBtgIjnZtF6sKLFPky9Vtc7iY0ZXLxdo8NQhf3kyUlKBuZoq4Mqg/0Xuj21EFw4Lug2X6EXBiIeJplV/HCe9fxfq59ypcyA0H60+pVtUYuQ/amJ1XdcpYC1a5AAUbdGPpY4d2s3i2nTQEOVEON/RapXwSY8exLECBRuHHelTtBwDlm7sjb8kIfHQHnWLqtqcnrwNOzse+ZitrrHivZnmUtJDyaFfRjcoecoSI/+feCwHEQE5mYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsKAAAAAAAAAILb/O4cVis/DloIJXLK6zg3y2uKNGph8FYoizUSjKzfb6nKQ7gufjpDqROnU3F6q/rbkGbK7VKWMESV44HVDAdEabfmg+yZgHyrPqcXoXgr8Zw2LAjTWNg3brj9Kuwk7eq1xsSQL2MI0eS4HTCls3v4D8X2HV2Flngb0/vlWWIC" } ] }, + { + "type": "Buffer", + "data": "base64:AQEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAE1J8HBgNQSQ/ZwFj7dtD2OFVEooj4VItPMQhtqRNNbKzUsO9C1ImuiFKLhUEzpWRbPF5IhgeD8VcvJnWWLyC/mmSNZM9dS1/9NmGo9Qb4KK5kqHj/Mb0DAe6GiM7ksVwIpnZn0aEDD07vohnfgpHRcSG5/xeD5G9fNHlO9346HsN64VZGXEJHvxEWdkpjPeBFvXGXtM9pbqB2diUQ1i1qP9W060lZ8joZGIA/PTU2y+GlCXP5WEh7fcoA6CYuebrQYF4/aEHt0spOiRa514jFHI54jbcpQxEbNFAECAPJLITLfpcmgk4Nnj1DqOEF8qQPQdYkkpzPsSv6vu/IhAlQpiveWyuazsSYFB4EP3dUb1fySxpHTal78LfsLeNYYxjBgAAAFAcQYJ3Bjq80IkYQGlRobMuZrHVChVRfKaZI8t4870WcA+2DPrv8jMttuiYGurEAP+Y5oTlQBoHuMzQTmmLOMzKjmps0sXGaTm1qyIss3xMqc2CC60VRcdEbk1vDsVvAaWwS+yns3B+uLn3GDgZ722Ak7R2pIXks4Lai2UCgmaaF74bkbbULeNB8ly6jDZl1ZToANqnpE9ExyZPlW1oJVaPNW/hZ+tZKI2TOKMWgcp+pLeIsscVA65ZFpl440aotBb30DXZr4u1RXEi1+o9X9GCb00T18SahJNy1rg6IUcc2W9Ng+cSUB5c8C8qAS1qKYdHUk7PMj7bQx1a7zqOYXuIvIvALh6LePls7Y1a/09v6iEEbCU0UJY5U7VquVMj+IhcfpvXN5iYcuqC50QPI/GwI5qN0R7cysC67bQEfKqmzf/YMYc8WUf8VBqX59oU9zduR9aevFzwdMNNNRFr+hQ8pbyJoBupGRQ1MYoLKHgsVyKJabsgvZvqDqk/3Wjuaz/IIuBWNVUMmXV/Cap+nM7vSXVynOxFKsZiupZJA/cYjW9LSx6tZP3mLoKCn9sJOakXu68vbOifl2BA22ksZO0vJXl9ZgEG9/uE+iI7mZn0aXNWiIkrwyUmWpFxY25Qeeo4tEkH6CJgDqMQ8QMfAcgW6n+//Ifzzo/TUezwoAjoUZYY7daOszTIhY6zKvDZ0O4weI3KryPDBbLwxd1Yzd04VnF/PPFTlMxg6hepb+CRfvQP5HnlKGKUQ8Uq5wLB5zvRp6vOmu2W2Jq/twaQlF8ZVOuzyfHC3oQpb5/Q9Jp8tBtlSPxp2Zy0STzNBJRcKFx87GU9Q2vbV/qrj77lqIoBxrjeDLGrSwIAAAAAAAAADQNXKeAIKXatYnxF4RvRddJ5EsDu4ittGEX3M350r81G1g686V86OMk1c5iiGqaVDRZ61tuvRWO750wKKFJxCQ==" + }, { "header": { "sequence": 4, - "previousBlockHash": "7A51C70423B28FAA99D236FDB07C41896C626A3B0AEDE5F8F77EF748B335744B", + "previousBlockHash": "28BA7F2C93974C90AA11A503B27C976B6EED085470CDF11A6E8242C77889C413", "noteCommitment": { "type": "Buffer", - "data": "base64:1VFep3fDG3jQM7IXUgSIc5yayWx+kTt+pWNuuFrQMWg=" + "data": "base64:i1hZd72Q/bi6aCdWOqGg4I1ixsl+M48AxPfbG/X5aGA=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:HtbB9rZdaTtVHX6qR9rSngAvUmd+ptTTlSNlrEciW1E=" + "data": "base64:FotkS0I+a4FhPq4s9AAPgzPktQ/bWyZezOo4EA+Nmsk=" }, "target": "9233318228143625020618577701423519925017621426082203201059080050516648", "randomness": "0", - "timestamp": 1717538843233, + "timestamp": 1717538987224, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 8, "work": "0" @@ -6242,26 +5054,26 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAAAH0GoT/7LrRguG0yKDNoy9ewEA4cl6FOB7BfWLne/Y+KPwu0Irz9uzUv49v8x87FJ+zAeEGJG5oP7xQynrTsACKlnYEfbgj5bJ4UWu1wE/2WqgRENm3eLMIzjgHaqtqavLzqUmHBTZvhtFLYAb8D/wqBdCSThiIRJKhQNbdX6gURCmWsjj6X9decFpaJgY3Yntcr3V7dWlYkwf5ZPZhJYOBvZOeq1ipMAPEXGO7DvQGPnO7StvBXHXNwv3yJp6Dq0k/W4bsF9H39jGKcYj2mHLjtdnI2ya5xOzhl5wHGlpFYjGovQceW5idwYxYkxesN1rmUFR8yNCyKzXcE4L7vWqUWOiTUjSMO31dx7vFGx41dImcUoLxjOH04z5N4CiNX6r+4sPmADN+fPBijO9G+DonljGDI+ZUkKM7D+uFSvj1LbfeWUHNjWY1Xl+bym4SH61AAhmnSlQrVG2yOSda06oqxhoblbixHTfplRfWTHBBiE7leiNR0LDpmKi8BDO2yp9yVSB63dwi1VtAujet2vb+4ei4rSu07tCr8aUQPguzmpUL5Q13YU74zJUvkPMRKVNfyFHEfjk6ygfaV8Y6lPkLAo1mqin5MUjW2g1dnX1AlC4ycEV6E+Ulyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwSU5gwUKD5ZDJfOoXXkRjRoaQRGUUt2a1nh5f5M6QXelciVGZ329dHr8qn18zI7i9AO8wFhj76fVFVE6IAxSXBA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAALVdeHAARF6+/FPvLKt6au1F+pPgisG9zBFVL4JYvR1m45/gQDxhSDVUzWnoEG6JvR4btm2AmemzicsIL48qB00kn1K9y5Djtm07eB1DBrCOS+1dICZZGpd75VzCRjLczVUWR2mAjLoHo7iO4YKiov2+nw/k9RSjomzHISATKZUsSl+fwW/+6GnEiPXAzAgbsKItpXNUVwXW7qag3afvSr0M1nCHHBToAOqUdpxSDjkKwOLVoSVcgYigagqIcYUhtOHthUFEIlTYIf9GrPexHctQW64+ktX0/LhzotMbipFGSHXin8Mb01H1GFFn2z2d28qlUCrHpdhdGLJJyhdeg3Y2ZMYqsA9YlMtsM9sBlutEo9wRNtOuZuBgvQSgGy8RYBlox+/u82x9J76TfbOiYu5zaMdbhHPKyCV04KEusfMhVOWuZveLhhLTdwKBdibzSEhGz7NII7mSomBPuE/oF5UnMbaaT7ANHAgQPOKPszprz1JtT0YquQ+O+9KGzSQy0UAapPRt+fzQLsvBldcarcoBzW+ugFoCChcDA2KmLsOhdg9yhN2kFGOEAKmeBN9H1oc/X/YxFlRPIW9C+sb/adN6TOgU7ZY+PW+KYA521TaK1fCkf/ZmIWklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwVnmp2/WjL5jliFYsJ6hCSIiLkZ7VqivBFmkJO7lkHQa6Io3ccBDSdQidWSK+Y569Kf9LBH5MrTsMDAcA5UkkAA==" }, { "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAsVj1yV+P5iuV3DltoHMXbeBgTqhtWHYNHhsilaFhIqi1oP4hz5GGoYsQM9XWNq2BQ5lr5NxdmFRD+umqSuiyS2CFexBdNKl3ULWNR2jhD6ajEmB6GptEEh9XXRVfhb7Dpmxaukf8TNPAMQQljeemC7IqjXgttaXZH7kwQ7l4xkwLbTMhud2VYqfv3zl0ubJE3uoDGU7aWNu3Hj7kNqg63Q5Q1+4sD7XYYL0YHXdgGiuQhDvzlUYFPPpLLUa8tUe9CWD/QwJsVr5O4uSfYl8oDZp2qCkWZOYhWgnDO/HbQLJn6GAFouwyQLO3co7R3b+DDYAuK9uFFWQoMXJWkUF051S3r4QcitnEmlRWGjVgaP8oogM/t/EiEBQuDk1UFL4IBQAAAI4h66Vpc9z6JfEJUi32sYFCoi2sSXWv11Y4IQGmhvZMpGSO3tuzyNJlsPKB8e5Ct7dPSnelCeUmxeVD3vG7t9Bcx7TO5hP9nHof8Gsnl7wFEJLeRJjnv806K7NkCMe3CJdZ85lljwu3o7837OgZ3R2gOAcmfGEbRHqdBxJSuYUg71uXN1gq8FCoWGid7Bkl1pYKjBDzfO28nuCaPvQ9NTp/jTbf0mzrLQM3a0Ws0RwzLrUMmi8/JeiLneSjcNLHVRDFSXt2UssE0To+nH6hDU9HTJLhfPys+YVn7H6oo0QVD/1d9zJoGS1ozWnIsVqRDKvGtbLlqgK/zPu7qJi13Hh4VAWAfttaJlIZ0piST44SWorUNih2EGYJvQ8einQJ4A3HKsJeJLEOpI51G62ZhwpQQyDLBE/0crH2SKmLi0bbihabxnQi4sq3GpxANiMZgxumZaQ4400I178uZNejXT6P9fpAZ3ZwhTN8/P0GdBii4ygpLsQ3NqgDmhNWYJFn6aY6p4jEmCL5O51R630EIohL6kBQVr+JhyLiLUGspwydMa6gE2KKoGN9o8hx+8a75rLEOZbP2wgCilWhmxx7tg9g1v08stL+UPIYSUlOVAPn3Ln9A3Fy+oOVsirWqRFYdfNgnFyBLxF+Glkftjg14534UJv4RgrDbnhUuF6JEiYJTh22OD7vUtEh7indVRA41guq4G2N+1KVjSIEXzPL8ASZ4z+lD8QaZgxsDlgM1BEadtt7eKKsTSvTckiLKOIdXcHQBjoVwRmAyOcSDMPzZrq7nXDwbBygudgrFDr9TTf0gTlAFwbYJTiv1IVw0iJGEYunMnHI8RDc65zSiZ1b7tKluZHxTqaTF3sIgCoSsu+bqZu4OY+gwUimLUG6FubybEh/0Z7JSRKfYFGhm25/njTYjU/2ZefXlfG+V12lzt7lYjdmxIdAL1wVxFZ9Yg63chLO3pls9OHIYE56jL8OCzrZ/RCh/REW4e8f4CyfJCy+5ge05F4+6YKxMiUkCOCXXHjz/6CfkrtDDgMkwEhp9Zu9qR38HAKhBJCQ4bTdwQiRurrLQ9PAwc6BzLzASAZABxvebd6TtIEyWdRt18wy2hN3xk5Ek6OJDVsjO4Sywbl9yvvjLwaDxKvcSPAJvttMZtEKkOOH/MQFE/3zWFg/Xqw83Wi3uIHIT9LYxjig2N/ewyDWx7kpUOoJ/OUHogVdS8FWQJcdpPeVNJLgE3PMqpGecM52UFDHRK+SQhj1eaffxMzGoJfPJxkoSLyb18l4Kixdp3yrBbUexUeAvEPmWS7Vojtwzui3lxhsnDM32UggclhVfKxc+LSUF6pX3eut9skn0FkHvc68l6Auwpmh0ljoJCukuXs7VdsBCYJ6ltrTCiEgtIguxe34p6L5ih6XGpV3Lha5Uzs54cyr979mIVcJzYRNrmkdD8LwytZwzqplMh5fijS4FFflsfz+QXIg2jS7olebEzHTt+w4xaQA1SKdvAWdtOin2EHnyrjAAUXYOtSq6SEXErlLMORNt3yS1+k8jkNE0RBUYn8UFacASU2theizMQoBU5bM+ENEkbZ3ohdRKEMuIhSsH7ym1JprAg==" + "data": "base64:AQEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAE1J8HBgNQSQ/ZwFj7dtD2OFVEooj4VItPMQhtqRNNbKzUsO9C1ImuiFKLhUEzpWRbPF5IhgeD8VcvJnWWLyC/mmSNZM9dS1/9NmGo9Qb4KK5kqHj/Mb0DAe6GiM7ksVwIpnZn0aEDD07vohnfgpHRcSG5/xeD5G9fNHlO9346HsN64VZGXEJHvxEWdkpjPeBFvXGXtM9pbqB2diUQ1i1qP9W060lZ8joZGIA/PTU2y+GlCXP5WEh7fcoA6CYuebrQYF4/aEHt0spOiRa514jFHI54jbcpQxEbNFAECAPJLITLfpcmgk4Nnj1DqOEF8qQPQdYkkpzPsSv6vu/IhAlQpiveWyuazsSYFB4EP3dUb1fySxpHTal78LfsLeNYYxjBgAAAFAcQYJ3Bjq80IkYQGlRobMuZrHVChVRfKaZI8t4870WcA+2DPrv8jMttuiYGurEAP+Y5oTlQBoHuMzQTmmLOMzKjmps0sXGaTm1qyIss3xMqc2CC60VRcdEbk1vDsVvAaWwS+yns3B+uLn3GDgZ722Ak7R2pIXks4Lai2UCgmaaF74bkbbULeNB8ly6jDZl1ZToANqnpE9ExyZPlW1oJVaPNW/hZ+tZKI2TOKMWgcp+pLeIsscVA65ZFpl440aotBb30DXZr4u1RXEi1+o9X9GCb00T18SahJNy1rg6IUcc2W9Ng+cSUB5c8C8qAS1qKYdHUk7PMj7bQx1a7zqOYXuIvIvALh6LePls7Y1a/09v6iEEbCU0UJY5U7VquVMj+IhcfpvXN5iYcuqC50QPI/GwI5qN0R7cysC67bQEfKqmzf/YMYc8WUf8VBqX59oU9zduR9aevFzwdMNNNRFr+hQ8pbyJoBupGRQ1MYoLKHgsVyKJabsgvZvqDqk/3Wjuaz/IIuBWNVUMmXV/Cap+nM7vSXVynOxFKsZiupZJA/cYjW9LSx6tZP3mLoKCn9sJOakXu68vbOifl2BA22ksZO0vJXl9ZgEG9/uE+iI7mZn0aXNWiIkrwyUmWpFxY25Qeeo4tEkH6CJgDqMQ8QMfAcgW6n+//Ifzzo/TUezwoAjoUZYY7daOszTIhY6zKvDZ0O4weI3KryPDBbLwxd1Yzd04VnF/PPFTlMxg6hepb+CRfvQP5HnlKGKUQ8Uq5wLB5zvRp6vOmu2W2Jq/twaQlF8ZVOuzyfHC3oQpb5/Q9Jp8tBtlSPxp2Zy0STzNBJRcKFx87GU9Q2vbV/qrj77lqIoBxrjeDLGrSwIAAAAAAAAADQNXKeAIKXatYnxF4RvRddJ5EsDu4ittGEX3M350r81G1g686V86OMk1c5iiGqaVDRZ61tuvRWO750wKKFJxCQ==" } ] } ], - "Wallet disconnectBlock should update the account unconfirmed balance": [ + "Wallet getTransactionType should return receive type for incoming transactions": [ { "value": { "version": 4, - "id": "5c3b31ee-84ae-4e9f-8d32-3c758ca02176", + "id": "c4cb3024-e8b7-434d-8829-9335377d66eb", "name": "a", - "spendingKey": "14202ee215ed69b97c6c3317793af0558ece43bbb2ee3f0cb164df9d8eb16170", - "viewKey": "110d3c1105b91392ff60c0f2ec143ed75738346f6f3d6a939e6f5cfd0134f29258070c1c4a7f76ff3838956fa5541f9bd8e3f5127b6903fe25efb07359c9a4a4", - "incomingViewKey": "af17c73970552539f2392f4caff51cd13ca17b635e01e1957aef25a55650a506", - "outgoingViewKey": "e1844f59394714ab1dee5d294c06f4b05884c1cb4cd78e4a68d25ffa121421e0", - "publicAddress": "e76c858e25070012fcbf5126dfbd4318959d300f4e0bd9e659b0beb1bf771cde", + "spendingKey": "14693cb6c6e5ceedd843f678db9bc375ce44c7073f37c9eed6d3dea779ab8bf9", + "viewKey": "418a47bf9602431caa7790bcc6d72980af04ec20426d099a7e0f212bccbe9eacfe08b43fc99d13b7f87c7fcc34893d010ecf193e57a767716b2186f5d00cd5d5", + "incomingViewKey": "6d2e22e99680c3c63fe31d6398793f9b87454432b01ddb8482692eb597d66606", + "outgoingViewKey": "6c88876709c07716f48b19d8056980876a7cd803854f30c6c70091577a6ef8c1", + "publicAddress": "6ca52578ba0d94daee94af9fe5603043acd6efd1b1a9e8c54647f59b1cf18f99", "createdAt": { "hash": { "type": "Buffer", @@ -6270,7 +5082,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "311edf69e8789151f2348e5705591d1511a2964e218061c450d24c9fa7951f08" + "proofAuthorizingKey": "a8b6d0e280b9332339356cd93dc93653375ce5aa88d013394e07a8dddea9d409" }, "head": { "hash": { @@ -6283,13 +5095,13 @@ { "value": { "version": 4, - "id": "772a244d-93d0-402d-8c0d-556b3f67925d", + "id": "20efe043-89bc-48e0-8ee0-07b0fac2cb40", "name": "b", - "spendingKey": "bf5135531a77781c0c6740e1ee5f2c5c88aa96e4ccee9efc30199a8ab55664b0", - "viewKey": "fd1ff51a5fd1f024a47c8e214de1cdc8eecaf506bbfac1e923511077f3ee43899300d1e4ed347d4cff8b9ac1000734d99e828e34c20e277b6197738baa3d7d11", - "incomingViewKey": "c7f1775e88f82bc6fadfb27c3a443063aa22b9dd692f3a0ba8caa77fb2b78d02", - "outgoingViewKey": "7ac5c5eb4054140b70697ca4e896c329c5fb55ffd98e1c1b352692b6adc9710c", - "publicAddress": "7788a27f2d1b3376f6d36b386bacce08a33724fb0565d47c6a24a5d0472f945f", + "spendingKey": "202d96f2615513c2ede0c1bff0cb0e5cc2625d1579f9de24c324097913f161c0", + "viewKey": "643cc5658542c1ecce532fc5a94e099521978a3cc47b5ce1552ec0dbf8128ce3aa620a8fecad587f018946a95bfe2678912b7e5acd932cf9988d1689722e7a0e", + "incomingViewKey": "9dff42b9726e1ac5354bae32ce2462d85dd985488ca3fad3579675b65cf4ce04", + "outgoingViewKey": "1801e8be9cce2b842805bb8d05744ed4b9073d1f2047c2e718c09715892d336c", + "publicAddress": "3bd5dff2753a8a2b0761c27f468cea32effcca7fa479d66abd38a70a32062a64", "createdAt": { "hash": { "type": "Buffer", @@ -6298,7 +5110,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "e4407cadd714bbbda77c092f4b9409732dfcc15842945fb45b073d2793a8bb0c" + "proofAuthorizingKey": "f942a40b3b8b18debe70aae80f451b1fd280934cac610a51a82fe75264946503" }, "head": { "hash": { @@ -6314,15 +5126,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:UVEq2PuNfdEoC5os2mTh/pGh+//KtVzV9OmjNE4u9A4=" + "data": "base64:UuhrkF4SAaKe7XkXXUMxX/eU7R+6uIiJXh0LwCbpEVc=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:KMr6pfCHWpaCUHXO9ZY4pzLg/F+5HDmahXtMNExDzrs=" + "data": "base64:KCOjP37PoO4h14Z+sNC07HD8JS12DtSF95A3Yxq3E1w=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538846941, + "timestamp": 1717538989717, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -6330,52 +5142,78 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAMHocbEKX/hFqteL3S6G/e3KMJAHNNmRRKfZVlnk9mD6wRc7YCXfS9W8uBzJK+P8xTdoTXNp59/yMVGDgBEQ3/M+PWGueOStYeTcPNaqW7LqgG1JlwwtvX73WEGPb3xq+bJXPnSuALtvt/hLTBpmNUKnm1hkzgCrfk6lc92bJp0sV6t+GGzIwX5FEYIa/98a/YJGLV+rhn9nl9WYXMpvu8ARL9pAZ0h8xdmvCpIZ79amnJ6V5KVGpyvI+rgNvFzb5YEMqfb3Hqtlbf4woIFdQEDU9y4u1lI+Z0TqpJd4m0ai1/DgpKZZY9LO1Y7kKmxA8a9EreYVxg5yCQxSPq0/GoTPacLy/TdOEgropoPrj+LG1ABME5Ywm0OwHHi2V6BZTH82Y0izewLUgvi40d6k2xvv52dbz9jhKBY81GXg1OqnTQ08jE/wypHRosTQQEegdxVrGuOHLU0HAnLvIbCxnIRgnHViHklbqaTU0y8jkepAjTdKsM8IOzsIr7aC/zDhNoRKjwdLZO3rmHJZHDZ5rqKdIBvQvoUjjBvpgyFXKb2+z9DyLDGYLLAUIO7+hu1dI/On9b42CcSEtGHc6QgUWY8D1dBlO0mGqA9NZ7EiEnThuYlSW1pdJGElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwyV+jz+8x2nGaHlf/UyODXXLpoue7fUuDmziRqa01zxsa7s6iH48m6i6opt/8hRYHCLwAt2trGPejvN0liJPyBA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA1K21zyIuRHaG9i/n5UhbPUTahRf/4H4W+mzCiPTAqEiwbAkw8MrpIm3E8pHrRGmU2HNXJ+H+oQ49lV9NIKD2Qe6okGoHDqIaRFMrSB++FL+DfoPlUyq7G3O8cmRHYRkQt4Fnnwl6v/QDhmDAtR4FnHQYlJKnw3rwSzneEGwlUpQCT6Hbc/KJd/jp5aEhQfmkXCB+YoVB5P9vQ/8/PeXxaa9xSLFZM5riRUjEjVb6WUGxP5wJ52DZcKSd2EFmwcf3bjSmUFYqloenbkqzGN2orjOoEQzYlcMECr9K3fXXJp2o+kvWp/BR209jz872Zirvk6dBazZvMJ40LZL9IGpGEa0eDU/0aJOW2oOZD+P9Tw4W9vmhgslgWhrbkF1/d7AZj+Xh4USFX55U6njxIS0ctalLqlASWo3bRmut2K7aaj6EDE/PUozwwftYUqSJNJv4zGEfGBzkAbvmlyzCqgrt5xXzS2t5wkEfklaNOIeX2t2Q1f/qQagdlCBSQIdRVEsLmrK2OKFub2zcQBI/RPpMDZDz4bXvBfvaBtJ46bd0hkJwn79oB0+IxCw9K/tNc8ZKYrKCLsBbikw5ejvKhTanPzXGv75dd/jQoYC9XZaC1Gy2PLZ2VBtkmUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBJSgfDD70PDdz4bvhExyqIAPyqo1n2ywSkzhWkWPV4L+DyiX06NGiIennDE2lDt3PLJ6vpbLNvCTuJp3+YEICg==" } ] }, { "header": { "sequence": 3, - "previousBlockHash": "47BE1B2BC6DC4E9124BB587396A9AB36A373166E8393DA3C22078A4EA29D86B2", + "previousBlockHash": "8F3C9A535641712B724F5EF936F8136F5BCDA81DB0CF3BAA5D619CC6290B7A9F", "noteCommitment": { "type": "Buffer", - "data": "base64:ircI02WWlTMdDjJj9Ca0t6e5NN7vTiBxmQmi3WRAIV0=" + "data": "base64:5XpURaP5TgcLXs7ttAKzKPPxuVeVdZsobHnDo2vLgzU=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:925HWtFRAfb3ctfSfvk9NYg9xQspfZWKVkx+W65mItE=" + "data": "base64:Adzrr8VU5KnMuAKnawAvcj2gZh/FhUp0FyXyK8lqUvE=" }, - "target": "9260366780148527510972123832573278885902566341756516011968728852484845", + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717538855857, + "timestamp": 1717538990741, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 7, + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAd60WzsCia5aaqjCnF6aJ1UydAjAz2I/pTIsZ3EVSOo2jU/SWIkYWD/J+4XSx/ExymiJXkc6Ej5Z7oT1vDcAHH66dbS3dX5QC+HxEwObJrGCEA/XjqgQ1Vdc/UnsZuEEw/ccoJWqL41gPubhQDDIXvyWICZCErjg9C40Mlpx/Q18Ksk1BMzVYJuVOSsWyVxwYgfR3EYCqFgrnwQ1Y1dqNKWphFJvpuvDfPH64lBpv9b2THjfG5A6t4FpGfiASD8tqcKJk9huNpi4QxkuEwnySxhl3xcNUablkBI7JCKTJNXVLS6PSwV6pOgZLg9QlnEW1qYYtpzTbBIImBjgBOipU1XmfzHF9UVbYz1IHyH8sihWIqHI7ncZlaSbl1amQ2x1GSqTrxZAZ+y0fSIYI9kZ6eSfnXE9yNexHeBOjCsPpQTs87GtS6byjl7AhclfkKHHx241Q/OFONj5I4E5qTlGH7F//93RI8G0nR341Fl2T7MnPaViGk+VW1068Zad3JYOyRvz0zSJa0RAu2yOmStFgVwP8SZOu8SDXtoqDrnVhWu9CbBl1j2U3i56Nm/YzzFivOT8gqRl/ZHItJDG4RbnyXQv+9JgwnRkhnEYq0569/hLbSYfCvuSMm0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwfP+B7tcXM8iblSEyZ7NIjkA+i6DPBlebCklGY03N80IoLIVFmvSTs00ITfc4HYEjGcK8Rg+spqUoBs//SqfMBA==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "4030109C2193ABAD2C1196BACC0FAD459A5554B2466AB2EF45D0575AE893A08C", + "noteCommitment": { + "type": "Buffer", + "data": "base64:j5RhH+NP7THvS7URjv95YuJAxQmARx7NlsFgETIiA0E=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:3ufKD/Tu2NIj6DUMd2pAp0MHYtxGs/Wcttu0MSsshXQ=" + }, + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", + "randomness": "0", + "timestamp": 1717538995575, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 8, "work": "0" }, "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAAyUcbRn9SkBycLj8GjNnpdxJlegIdiiV5CiEBe0YQ/zOqMJiEGG4UvJ0Z/Zx817eRrxSXGi/AgCE0YJqBcmylwQYh9xe06QBCXY5SiEtnDzquMh3B4wkzgL6xJ+qHol5lsgmEJuv/Gj0oDdvm5Oe/AkoeGu+vJYVemvl3C2BCqpYWolxorcwum7RB+lfs3gGNlAGbytgmjh/HoM0aG988Lo3Bm3qvu50+Qg/7cBqgAZ+uAlbpCLwG7ZT2XB+GKQtI8F3KcmzovUeriiuqc1R2TfwmRmfGu4zJ4JiMJh83QQ7L3nMc5tBmZ3F1xGHAvV3Qpr3Y1no9pZ7ohrfg3dM7TtRs/olo7pJiCBOt7TdDGkBrXrte1/ojEuf5n09Q8QsMjGUmcKlyXHl64O9nc0a5VoJwjfRoVGeOPqs6pf2afGZMic74kYMuF5HI50TAwNwzYg3EMifD15/t/Lg/ychFI1hPLtHdHswV7nLG/4x61ia87dcFQGS0UO1ePKgYad+LtXlvX5hiDJlUA0DRnlUemkJCW0pyXtoQcYEgtD6bCU7xQ/S/4OnxuKZf0HFOYBiUWGR71+/vmC+l6Y8QYs1RDc2GhALw/juhwtDb0REBIwcdZEn7hM2E20lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwDVZm4rAZYBWbolDd4gzVsbi0PtZuxU/ccMWHr5ZkL2XIaJJ/mWZO1BLEUNpSwLyIzAekacg8TMNuQkAjGkwsAA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAAHJw1xcOYxGD9SzcMislG4w/p9rVrkr+aQg+SYuyZPRm1AGU5Y6frFpwXvNVKJxPRtGPMGBkHbJJfe4Fph8DLjAfxQ+4T6Smu2BwucDTn3/CAyy6aJt8zwRuWt7kjvL1qXco3PgVr1tW38pVDdDTMX4V1gNIhCZM/8xSjPly/UhURjqWieS2DuC/glh66pElUgoVH4GwqN++85C4h7Qtdepct+FgHYT9pAsUreVnvavCF2ba65DVhvhei2cnVYr1Smx3iUwemI3T/pjU1513Xh7lZPiwKgw+vpbMdwe5P1MOEyQDJrG3cUVgoJTP3d4bQ6V9q2zcbOmmM6Cmva/G7aBB2RlgEwT/4Dx5UKbnkEdZPILTpnXdEAjXm4r/HvSc9VqsYHulj0g7/KTR76fyx71aB4DmRX935gHtnmdx2NoVEE5vxwGRKmfwnIUfFmOroRXLFeAIgtMwIAuV4TPWiIVkM+xAm5q7nrhq2fJriMQjyDDFuyALoEvnqpyLyKY9VvTlkmEVS0pB3OSn/KK4HApF6uTyev6cPtwOho6zd/nl0kllfeAm2Qos4Tq5sp9tI1FBqDT8KJtThHQ06gHL8UTo0bnjqcmWL9nGmb/aI/gaJysn0Cox1dElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwhsNBOFeqO40AS+WoO72jhts1jCfAtN5qQVQ1Hf+MfbJW0wQ5B5Zz/TKrbnx3c2P5n8l9cXWaVATIjL/xePWEDQ==" }, { "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAcIbTSj5X5dwiPouHLXYI5QOajW2WwbB4tun+0Hz+Fr6oRpcakoIL6bEZQfx8AffMLLEqueiTk6r7dkUYnVs1LQ0z0Xy23g3WUx/5WJEPijGrdJ+avye65OZ1VWHuCPoQAOnn13YYhiaxLnWxjiW48YofBdDpkX/r5kvhT1EO6SQZJ9irTSn00O/nFq4CXxcnMRq/DhgItM9WCMIgiKP3VH+N6fnznBNtXVAZjyjfrKOvGE4qQrNdvv81Zsx9CTbR17OEa6+NAN25AJLzw/MiO00aufLiI74U/UyFOej6UcVjsPEeVxtN9GX0PbtpYCVG1mvF8QJjuiT8ufzsdjByJ1FRKtj7jX3RKAuaLNpk4f6Rofv/yrVc1fTpozROLvQOBAAAANpXX8ersxpuq1AKk7y0dOwLYjGJ2ZWT+s//0TfZkvGzW1tbnHYgRX/W+FCIjcOcG/7yJY4o1kJR09BMQ6bvD1CZN1MVKHtFAOHpcg9eP8lWDo5NmEz6SUKfHzAS9JieB6SZ/ieVeyT/dgP9FnG62ZuGFSeh5DJuT2cpk2d6y48fwMXJTRdsj4eNdHFtkGNT74U6InHGMAOU33NyXMVC8dRuOH3U4q1Pglr7EJRxJFqkoK3OdVOLb3Xia8eTTg7qORF9mIZpP1ZrsILlKcDl0lOkCymNLVyCSAbFDXM3JMOboEjr+x0HQ2L2APH5owfDn7dZpcYResuup2S5ZwDSXnjka0CzccXik5LQLMIENsjE4gyw/4qQNY/J0HOGzMb4NfFK2lkUWmahs2KCARVfYncdfoaaxiBDFb6AsmbrsA4AEXEhmUQFJOudAUvH525Ef+ymLd7A5sjTAQX+6GmqHCBan+bDjB04PRJwLzTgf4zILtKhYYJtLYWdl79pNCYCgIsvmnPoG//nZ5ueBd1LWODMb7nUdXgC40lQ+yxu/V6i1/OquA4OGJ0Ih+bltKK1qqB+4qPyLHUXwhKpoqQa1uVnomVxLdGA76RW2NRAJHaqfvhJHoRxmbYFd859/ikFXi+5dXQwEVQaeK8NbUFuVF3hKIAyhqYlvsV1ziBPqs+pj2RfDey3qIvmB4kFo+mLqi/cC0N586CkH64HiQo+vwkP1n6xSgc5FXLTk0Xta0Uw9oGSgvdqgx23bByfasjCm48oGwoVxkWH6uK+LcZUPeEqHKci3lQ7l65g9Mf2FpiS0FbcjIlt2Gyl3j6KK7yvgToJ6cM7zwz0QhJJho7Ua6XamJpS0sYMaRRcoM6IXKecVXDFjbhBzW6IRasvSmuG5quxculPDHm6S1cheGSZA3pNTz4gnT+F1yeW68vBPPzRUYkpMhLf8LkGrqxN8ctozcDJghuZefipRb2n8NDfrV+ez5npn9MH5asGJx3C9uHx3Iyv4qWSsL2GORZuTnSBmTNrHf7K3wyK3JusG1p4Eh8aqKYjqOyzkX2dwQJ4C41uwfCdwRsLXZbYKxpN9NX/SdpBsaiWUbQl1ocq1QYBT53rSc7Ngh2QkQWMMisdp8a3KIdxdPwKxJuhevcs4U4c4nXmtObcmJ8DKzSF7uASm3ytjYEiLp8KQvyxslRR4NqDokJcKLyCAB1rNzpGJeI6sASXZKWP2Wa8BVDGj5XcsTICrdAOTi02KRLWuUAIYdX1EQJ9k5hGkLSupIbM4iSO0cTpOEfeaHoH89NBUL2tqHcq3hfD7gNEBSYWjX471RFI1um6/EOeMWl5yyXHZgRQTzkAHjgrsHouSsmrtRwddNWYaI8/kFV6Z9W3fZ1zYKhs8ajcHA+GlqnFMF/jiErw1tl/5XJPDRBq0IZM4V3amDiC/BouZpwmQgFKzI0iGxl2qHIBjPFRrXDHaKCBURrH+876JQWdyWa2Qof2PNTa+5IhG1fW0fKW/7sszZUFSI36muDfed7nlnCCm9/EDI6oOVjXEmURVSoN/hOYp2Z+MuasGAFqDo/frtoh7sdelK+j/325JHVg7kGmb3g/FQ2bCA==" + "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAU9DQfBkBhR+iUr9GxTbNVob7ycwXFBHQbXwz4HSReCCX6487hT7JLOfPVdd7qyCeaGFRLssZkBw35LlN7TU7+3jbPMxOyvXwBmm2KBx6QR2xrREdBUXZZlqWgvshWUtDQ0Aj4C0Vwcj6H8nfb9oEVCsKPSLQeO9vhAC+n/hBIcAGS63PNrJOm/pABp0KGelVKEYSQ+AWz3ftbfqiQvVOXaqSt8OIxB9qaOBor3kQ6umTEc1Yxll6uqsTEVz1SKlNG20UMOdpQwjGzFi+e9ehrmSqUXun22UG1fIWg04emtgZAbF+NXSrmLZhkuTr6Bp6n65GmGLFYoy5YgUN4el4guV6VEWj+U4HC17O7bQCsyjz8blXlXWbKGx5w6Nry4M1BQAAAI/SmkkM5k4vLO5PR0Ph793f8pUZEC+ILXBXOTLdd6PhP5ky4/WcAgtGlHtrIh9QAuW1FFUgXyt7nkMu8QWkx7zDhcFjfczgrbRjPcjTDeRZKwLGdMgFw8geqHy1StLPCZcJJSKZJqrMga/H/S/Qw62bSrNJOI5SLQz3mtLQSbC0/A6tCuYSYDL4dAPyCd6sm6gNoi/vb9UOfmE6h6/Kvi6wayxUp/7wr5+M4Sfe5lpLW6OZCaJxD9eC7eSB6u1JVgzMFGHHuGihZ0hbx42jD4mFLwKGxtytWLnYH2qO8G2IkDVdx9eQQH/sX1SbCbHr3bU7mhf5zJQXuZAgChjOjRlpSIHWWVgrzuFt2WCGsGqTaszU/k9l2O0pXqdHm7cysZg1uA+Bo8lZ25lAnVMJEZorOi/VcbD//p9DxYXpKbGs9F1/9c8/hJkOcT4tIFaxZ9vWMEaN2Mh72PnwV5+Bg2s5HjgK1ztIDMOptjID/WDgjGmT3bhkRCpmsTJYQ3jdn8cPa0RjriTVoMzUMVWNYl15gz9Q+2CDtoy3+bT52psTQeLSpNo54JtxGNCW8ct9v0A/6mCf/9nYsJGIe86Xez9J+R6vAplqTx52lcqc6tH1VhVrzIEZELcslXEMJnjVISJjA/GHf89FTunxkopoPS0aBCGYlsfGAsv97340psZuF6j2xXJh29MLPj1DIQRoQWBY+b5oMd4nDemJuN+rYqp7oq1Q3MS2yug3nvVS0SDRHgBTK2AfRZRU0yXJUiyqJaunpBjtq/1TWVM/TRjsuKb0aJe6cFn14tnpunkyhx0YfSP2YkDr1WmQ3Pqhdd6lj4Iduk/Og+LAeo/AxaqFueKBED6ZRsUaNxPzehA5DZ/n6xs3AQv/3naEuSgmRhW2QF/L2eL245ePYLuLm30TdjdgFnA4DSeMbEjujtfq23iPEyt3m0rjTbsBJOr3FXzyaYbF7dnfjsIaoUu+Bar0A3OfXJBcX/3zBLUj6VNK+A0WX7ceBHWtjayVoJfyahmj/FYAFswYsa6PutSHgQ1mq+wD4vZIFqeMFelynXWWz/aGjOHZ0AHedkLfb6HrR7Nqd9HVO531fRDytf00wIecZcSPFvSLWDestcCMu0qxshKf2/t99OLMFWzwEtQyZNS+hUGf+1I5SgRMI7vY3E0SmA5jDXb/EfayXxoJgDxedfXXrqeO88zeFykcQoI0DV4Zts62pZ98rdLCzV2COQrPj6Eu9bLqTImHMWrAh1+tVmyotnz1fZEpe+8eWFznAL8C9l75eBKSl2aHTlrsUOvdbOlCawP4XQ0SK9jtaRzwSxE2ibBRvt1t9FGB36D+355NlXK8olT2DE9G5I6uC20KN1+StDZGlyn931FcziYp7R1Kc05wXGCFIGKlwb6kjyZ13IMY2mzP4LzWmycg2owURFgdXCL9+CUGO3NaBSJ9RzlFUunq7xLqNoHLz+PrNxTmKFFXlBVyGoD3hQ5/W7bfGi/C6U9pAHInIgjId+kZnuiS97ykO8+veG8387640rOLWl2QOZ7twQSfJt2ytvPi6gHPJ+5ENAxMzpnfakuThJZjoVtbfITANU4HRchbzlfYAw==" } ] } ], - "Wallet disconnectBlock should not disconnect blocks before the account head": [ + "Wallet shouldDecryptForAccount should return true for an account with null createdAt": [ { "value": { "version": 4, - "id": "06c2b10b-d498-470c-bc22-724c216023cf", - "name": "a", - "spendingKey": "fc8ddbcd9a14a83bb10605aa95d83327f72c3113f1e0c385c2142bfdf14ce5bc", - "viewKey": "fb1290c5fb54837d30953e67b8cc00facb268e284af9531716846f9ea0606b86f0b7241488e5aac31e9160371939c203410bc7fbd094fae09160bd0d911912ae", - "incomingViewKey": "8a7496e08240c340567d2689c03fda557d0fafef3af918b0cd23bb6d2f637004", - "outgoingViewKey": "4ee2db77355e1431706c7cce644bfcb43c102a7d4ebbfe5b5ea70685c17e3a5a", - "publicAddress": "cf135210deb44de9cff4bf804acbdb5ecb8afdebf15642b16b98c4283068d62b", + "id": "ab5b44be-38b3-4c52-8872-8f93b269b817", + "name": "test", + "spendingKey": "ba906d52efbd927f0be11d429358919ea1c87fbb7a4498f8e2ecd046a2793a71", + "viewKey": "fa278f55c53812b3bca2ddd7c0878050b6522f9f87288c3d03250fba9a5bf236fc5e71eac8694878b8c810d96d5c5721b9f1b3507a86eba039e893d09092e89e", + "incomingViewKey": "74d4b2b2c1cc39f4097b90c8b50a3bae0c85c04a3e588e922a18742b70b53304", + "outgoingViewKey": "7f7a01e7efdb53e7b4a12ca4664c0277429205c6b21e5532965f6add9967f36d", + "publicAddress": "74f189128d5f91e129af0bd86d986b948f21ab00561e7b47285000968b8df845", "createdAt": { "hash": { "type": "Buffer", @@ -6384,7 +5222,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "5116aad81f2c1c4cf7a0132eea2889d7541554b366731a448c9a656883718607" + "proofAuthorizingKey": "d4da17a97007808a389d636669d59efcae825d66cced3bb52c813e5dbf9b8e08" }, "head": { "hash": { @@ -6400,15 +5238,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:oIISnC5yyLhTdHruf/8Hp5Hxe87Gpu1+5b/zg6HbHCs=" + "data": "base64:Ye223RdSL5Fj+ilufxi5m1pJ2cQEP04YtZP40xumgT0=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:zspW61o33712G2RTjQUZMzo6yzNBI0RZdWfZOz1jsM8=" + "data": "base64:VuyFBIxcZoBgF75z/D7ExBC3xFJKkzk0ASQhfIm5fdo=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538858455, + "timestamp": 1717538998177, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -6416,48 +5254,22 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAR8+NW8/ifVihj/Yv9j6yald53BZyqmYv2igRjH9ljKi1o/U3BvFG1Iq8AuB3aVMHvyMp0MZ7qEEnON3bLbHKZgDiY+d/z5bkigCRNZnDjAezmDeaOcIOMHCRYUav4ppMzAcxr6HhE7zEiyDadJmgv0I3vkO4uaCbXNcHvFfY3hQRZkF+uOFkyqGjGwaNGu+KUyRvpc6fNmwAvPzZ/oQyCBR3zqihtGHmKUSgWojelVSuwDckcHDOdzorpeZ1o2Dk18xLQQyX+UB6g7+L8ivG1h7XoXzfH4rucoC1qXPzvOKdaqOyYjeqhSbsPjdjF9D59k+qyC3q9if6DNgO7dK+aubmU0r6cVaTnTgY5wi2zLUnPSCTRABQcRLi7PFv2sEWTcQeK4JfP2FooiLIUCb/KvzCjoxpxaNOFUAV8HBZkC2D0umxZ25FYMHlVT+eFB4q0iiPpIgDq+UlmzYdfO7XWLNM7eQJdHaeQBM77CHWJ85m9iwjedg/ZGIkch7dv8XqnbqnR9BwfXng2/FtWZvFFMusG8RGck9Ip8aEwIZDdfdszV1sXCugq0V86iK8vZrQXUs+Dbg0UHUitVkC+FZ1QorTp+pCun7lwE1ngQpoajc157Rcfnx9GElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw8WA2ieOlnP8TotfkvOs4SQ1gwScWSFnFjf9kqaDuvMTgKqJy4esCCZ2NezZ0r4P+sxeC4cNv2qmW5ZJ1UJjtCw==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "D5CB6968AF11661F9A7A785EDA2CD889F40F7111C3E8BCF954459BA888B9DBDF", - "noteCommitment": { - "type": "Buffer", - "data": "base64:TyeNFr8FNbUG0C2wCQe5jv+8jXWOmJVNSYIYkPiEmlk=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:QY2eAdcGiBIKWdRgqsEVtCVf2BWf7X//E2T080QyOuE=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538859907, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAATicJNlPBMAX96+ROTIYVSblPyu41TTNrolZycYj5bMuT3jVrwQ8X3JAFbQpoCd5yANzw3KejzYeeOb7De+SAi++JzIn8R4CJeN2U4OTPSS24pMv7EZM8E4Nzhsur9rYpV1Bw62SpBX+At7hR+Ywm9vvZr+VfnVzSkl/gphueVu8LrIK9U4OiK8YKZAzOLKBXrS16NnFQusF3A4O4s9VByv4oQUYk57nuYBctWCltcYyOwxROejWRPJRRCd4iEn4vPlhRFU2pJt0qs6JOL6seev+5w6NtuNzo/fCCu2KF3YS7OcXT1mqBwnRfE4rl7RZLYdemjR4vZXjtK0e+VJxm8tE1hT9WQpPrgnR0bTXX7VR+TmBal2ItKvpHfWgKdGReA4TYP4RgeXYvbsLlWOao9tn9jYjUMFvPPDSVTeWog5rcT4C0ZnJO5rrrr5XVB/MJNNBNxUiljLq/ouMdofpuUwTsvbNFNeB3spanQiolE0WPBpESEkjDZcAmyEi2h8Y9k6k0IzdiNO4JKUAPAfwH/UUlP9m6D7iju5UUpgT13KsVC1q/wEFrYBjOsXZVfN6ICc9mNv9LEdMhJt4QzaCk+4Nuzchtf07GuZW7ZMuRbDhfQtA6lPTd3klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYVlXXBZYDSff6CVvwOFkKAIMRuRTMW12CxDW0ZvMBoOlDKU7N0UAeUpqk/50o3qQlry0hDp7zR9ztF0BC2V5CA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAtaBpU33iI5phDHMDr379VGMo9mtOeclLnyNPE/Gk5DWD5XURR1gDcbaRZ6brJn/o63t1yIvzMq+mFjGuFOksTn4Vc3VUMFbNJcRAI4x4iBm1EqQTd5sA8eaSeb9lz0bMpT/fXduflL5uKiSHJ5WIlZ5zeaG3DVvH5eirAN9kJ2USZ9YJUA9SsDK/wJPlqVm7BL0/ph0kJ/UXL6pQawgLiHy7w0lu+wWEtX39Fc4nnB6ZsY6VSb77x5Jg4CTrX2AK3unixfUmZFKutH5d3DkGYKLwfIoIMQmTx3sqlbHHeHhCzAJhoLhYP9V3FE7rwc/KOjMitlzx3tfWF/loGkfrz7nWbaS7hEC1n60pu6SWwxQa1ntzfbFCpDfghO1CCWkrB1UDDbAZruUIuCLu1gqGGD+NLZMLed1KvcYTlcoGH6BHKmP576imyeZbSQHLkUWBBQ4pIAa+gfejAcE5asKsUt5MMjVa/LZsbt32mW707oF/ZLJgPOmMoFufLww9EB220o52pYQG1bGshC4agxJik2t936biKrUn+cSFDx7ODmpo18U4DyfykcxwCLbrcYkMoHX8HmyiTzhc7NIh0P95WpvuyUwg/UasBxNbxb6mHgzjck1zZQ8MIUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwVBoeIcfVogURg03ma8kl1MPPqMaSumSYTJLxHHmyK+f4idC1ofgBJ5PJsOd8NFqNgWb4t4NFwv0xkcV9jdloBQ==" } ] } ], - "Wallet disconnectBlock should not disconnect blocks ahead of the account head": [ + "Wallet shouldDecryptForAccount should return true for an account with createdAt earlier than the header": [ { "value": { "version": 4, - "id": "d985e9f3-f52b-4032-b73f-2749d94ad877", - "name": "a", - "spendingKey": "341da00743442551cfd2e4fd4b68f3b2a301e29b888cb1db28dc0a67b266f86f", - "viewKey": "ec6f5f4687cd114f2c7f8ad0094e23ef9490abc9690787b820ab7a09dc6c54590a8e84c158347b4f731992e730499294869b077f44d936e47f9403faa6be5098", - "incomingViewKey": "f636c40ba00c10270a2e4f47e935ad31ee0a5ecb382dbc184408aa0ab1402400", - "outgoingViewKey": "6bf7751d5aa48ea23c6ff551afc76814e1ca5da64e6f50506505c115cacd6272", - "publicAddress": "3ad02fc5efcbb82261f4e1ee206e30125ec84b708ca463f94cd0a8f60422b563", + "id": "e4e8e6e1-2e12-4718-9113-80c11fee565f", + "name": "test", + "spendingKey": "8803cd390dc21e390155bcdb76703829be72bdd0d34883e9767b3e9a7ecaca3c", + "viewKey": "911c59f20fe23ea928efe890c6a3fafbc026008bc2efd219c61b76a2515049038b81f13675c60d7a7dcc024027dee03d3680e79c39a5067814f90fc9dfac5b41", + "incomingViewKey": "ee7d2844f24f69e6b7ac13b9016cdf6ebfd3281ce832bf9aae209485bfc02004", + "outgoingViewKey": "494d3e951b10776159af788756fc79169babd1c44674fd36abcfade121882735", + "publicAddress": "71fb7ef377bbe2d80361b858ab2d8cd2dc526bd5e1591f794e7d213f890d1644", "createdAt": { "hash": { "type": "Buffer", @@ -6466,7 +5278,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "f3291c52c0b33405b81cb6a60460c3678a169d5ff146e595794386980f77f308" + "proofAuthorizingKey": "b8510ed4d78dab26b05034300b9192410c0f91ebf109c60f9d3261835d5f960d" }, "head": { "hash": { @@ -6482,15 +5294,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:h3iHfNNoqk3ptWzTCvzNuUgKVkB5Ih+6h6qosgoCuWU=" + "data": "base64:CrdKzR6+2LH4Vt+MAuJNoo7NDeOEBXMeHt9dh/7/zhE=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:yNNo/jHUN0qcdfQ7tay/7TRILJjOnfBcPL6Am9fSrXA=" + "data": "base64:6anoi2aMrsOrpvlT148T0G5FALnPyYATB1ycah0mQx4=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538862519, + "timestamp": 1717539000204, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -6498,48 +5310,22 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA+kBBR3w2770Gg7r/fUAdw93keYoVlRD+i3dd0fpEl9quMMUyYja2ZtZkSKh0yDmbwYb0g1tF1SZDkMSHZqyIUGZyzHgzRZmo7MsuAFSMxWGVxq3HzE7gqa83E6QLdXc3CwDHV9FqO3uCuqUlPEbnntJeDeA8m3+ikD2K+e32iDAUncE64viikd4bxDXAFeedJEVKMGvdzHaL5LoLAbQpqHTE+g6DfP+HMNE+/QAtgpCJyV56kPfxB6WJNi6RxsQo+CJKwlRO5K9KPwKvgH3nUfH0J/+eVrAkMr4ZOdyqICkAlfCHfsIJYrznWgNYc4vROcLATnwu31XwP2opVrHYsGyRpIm35gpc6ZC1oXri4pxRkIXYVdmzlD8J3buEFhsQ8U5nDBo6G9a6PN4qbZaCXAIMXP1ZlnariHDjC8m/xgTZ7k3xeveOS6WIRQCnOk0Hnc3aKIRAhHoRy9WhvLmcxOcsDWKz611NnKdYakPHtSAn5I+8GFOc7ORl251FuNXU7TdO8mmYs8dRb/P6niwf9hri/xfX7a80CHpvcHkaSEe8OIIqw18vsrEkk6aio03y4HHqhW9R2nsz+EMz76r3jCvm1wikvRrDVOn96dRiLbvyrx72bJFX+0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwGs7nTOeoaOvIMJvM0DyeKLMeuJWVRIspaww4L73ZdwNfZ9Xu3XjdI93Yyn1yHWqGh23rPsoV5udQdjBUBJFPDg==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "E48E0FBA10278DAF09A70F9A42BB91111FD244BD9C1451BDEB6B84EC3284873C", - "noteCommitment": { - "type": "Buffer", - "data": "base64:85xP01XjV7CBMD+YPMiqLt5Ta1jsRI6avi4oScbg5Dw=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:bt4ip0u35BNrDx6veGbcrD5MBei91dNWMiZpQbge8go=" - }, - "target": "9255858786337818395603165512831024101510453493377417362192396248796027", - "randomness": "0", - "timestamp": 1717538864120, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAJm8DcH4rbB7Ll1Uod57AASBHE6oNjWuNK5imEIyNFGeho306OydbtbVn/yk3uhLlODCsxAticOKfipAUrH6ZIBMaHAE3hkSKwejHx+gOLI2vcoKZZu7r3wdQZEjRsJPtOdzlLFmkk+X05/BjXjmKUpBtLlmEKVDThqhe0wlmmlsNNf2qWxwQ+gaelJbS7E/7HaAQ107V3eZsEntJoLErgzYNsocwAvRNXloDTsJXf5KyxVMU6Z8pIF0TaTLvldZkjeDIwzVWAzhphPOOoGNjmL/QlgogcMn4fMxQFSHUII1+4ykj1DNBeroMvCva7eQdX93LspbrLbMXpqU63TPOWiFYPjH/2Igk6PzIuFMIZevnw8nMgGLKJTrUvPVAm3gOXIXHWXo2J/4m2mwyfhp9pT44e2IHIObjRlhLLsf9PtEo18iPIFg1ZsD44q699Gs+4nefmMlOm1XGUOOED7jmKweY8zbsWCkov3jAtbnbRNcL69eLHZ6Gws4f3ZXY4NvKmuoqBHz7G24JP3CopSgPamgUgIsyh4votvRRdl3ypIwE8qvNYvjPsbFVtA8yWU2FRq9iINZW28YKsM7N2xJR5GxBpgJYFC7sqrbSPJ6vWIQ0O7LwND4RBklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYOzxfJdc6cI7qAXgjadd9STVhj4CUYgo4+deMLXXyZdfpcL7LSGzvxy0r9XdFJgBtFCYKTr7vrzATmpeh7K3Bw==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAXW00UnSpuO8qpo2Jyg3B/tpZdqG4VCXnE21y1dO3szKRW1IX+QU5tTiRscWoIIrLesCowNl+UW+H9Yh9c2f45nHsQ/UJMXrWQuHBc9B+SJ6luZht+E60UMdqZ4AiBwKyWpW8qcPo4W1huZaRjjgYcGA+0Hg0dPCOeEFKyc94wXoZz8eED57unrjvPC76XQJP8MX/15TOEHpxWEpvuprzT6W4lkHbFlDS59bzvWH/ENCnikKBV496LqMVOhiLvxPYChvccj4fOOgSYRLCIhYMS+n5S5XXC4xSI1JlVIuQDOcwsO8orzhg91Lo5Usyygox91+tBlHT5nUutTvPohwJ7xJ/1PnOrxgooYr9izO2BPGLCCdgeshE6k6nOhFoYsULxum0KMGo/tpWqRsKwQPglpenbW/M6IqBQQ2ZdOVoS8l647TznNHsmY6qGgThHkLS2yFqg1F3DDbTvSWXeUlA1pAAMF+TzD96lPWWJxh/UrYytkUjZyW//tKmh+OacfIaXQvCQ4bqO3a1jpQVS3vpe9O5Y+2DDBpdANSt17LPTG0/vuj/aH4E6EIDXjbMl6EWWEySd9l0M+H90qZ5yJq8OxeJj/ckuwkp+H+Qi2YbtMH6zgllwp3k80lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwsDGvyh14nM+TXNBMGdVKsRDxztaXSWvmYF5o2hMV46DtRKUv3XCBNq0mybrGxc8eC3YxvrX3YhCRaIVV/n2/CA==" } ] } ], - "Wallet disconnectBlock should remove minersFee transactions": [ + "Wallet shouldDecryptForAccount should return false for an account created after the header": [ { "value": { "version": 4, - "id": "9fcf3b16-1c4f-4a39-8b86-2f3fa8072833", - "name": "a", - "spendingKey": "9ace62028db6bc54d16c3773f6d42834e008b24078d7bb5401354afd15e70040", - "viewKey": "eb66405a64ad09358f93e9a38c3467b4bfa0a7d945ce89b09a53682049024803baa1a59434dfd01f668da5572e6fe80dc40f0df4fe0acdb6de36fb1e16c09eea", - "incomingViewKey": "0c6d858f2365bf1de92b145432f2e938cdfce1fa2e0e9168196b603b84645e00", - "outgoingViewKey": "9ed004a9bd5c88b0cab75aa01c29916a40f67a07d3c0bacfaac041a5e06d7cb5", - "publicAddress": "9a8787574625ba72f6bb0707dc3d055bf29b9810910f0e75ab20d64a1b0e2610", + "id": "11033864-5f47-41eb-b7b5-91b713a63239", + "name": "test", + "spendingKey": "a86f0e2bcca2fabd80d36b0fc5095523f3161e03d179634f2b8a8b0aba30edab", + "viewKey": "117029994f58adefc5f0ec01657d85e6eecbf6171186c493d16454b47d7f132d19992c4b5b8ba70905516f365b9907cfa483649fff11272f6600df2af631c1d3", + "incomingViewKey": "d64cc966ecd93fea10610e78803648f34b2e9d084ea9dd909e4a06b8a126ba07", + "outgoingViewKey": "851ee2cb193f56a66e242c5d2ba4ace7a653fb1d6b11476cc78d98e1e0d3525b", + "publicAddress": "132e02ec40e55f3f0b5e53721f41539e2559c3da3435a4128e2de74cc6ec9bad", "createdAt": { "hash": { "type": "Buffer", @@ -6548,7 +5334,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "c2c64c5284b54d248abe1bb97082323ceae5448b78f1b647d24095a882453906" + "proofAuthorizingKey": "18813dfc16bf034f84e3213c1e7d10d2f5b86da87d0520e2e063b9c899095409" }, "head": { "hash": { @@ -6564,15 +5350,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:BK09Iduh+yZA9TBEQBvakLm/dVaT+lyl0St9px8BgUs=" + "data": "base64:oL6ChZ4Ly5JPu2d4tRTaerDeczEoWTNMR8BPSdlyC18=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:xLnOApyO6msCHA0mKFC6QfObZRJcZoqWL8OQNfrQo0w=" + "data": "base64:7pYycxMVG5LSCil+Dr0/ffdPvPBzSKAsJ0BS7vt6xTc=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538867634, + "timestamp": 1717539002251, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -6580,22 +5366,22 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAArBIIeopY9Le/A5S2FpsUqFDMU2FQuoIDG7SJVsnKfDaAzymoWjz2esM9yxhJedrKeku53CULpASR1jDhQTsnHf13h4ZkUlFC7wJ1BSygRsCZv6NCdwnc1dNb5EZKymY2kj4sBTmS82T4xnyydkfhCaNaWKZJz/9agL0WkUlq+JcAURDoGQ8Wua57z3XKuUqTEMXYe4AsXvfbVVhO60YRIM7tGC5DPiDkLDun1qoksBaKYMxmo+LplRYE1NYiuSb80RZEdadBeBmRgLVyEx3LxljFCLYHW7xUpYOr+qYRoVsl+RCa0HSXrn0mhQKUYB06yGlduKN//o3gYio+wW/tcaH3/rMIa8ZkrkTQ+nzfDAm6a0J12VDrSAdestI1LTVxMjl3hLpXzvtl+IfFVFfEqgI9sDYq72/bDF8mIF2xUoWBllYhuq8i+YXxrLrtcOkzJ3G0lfEKpwxfB+kHS96bValK1CsTYXHr+jbI2DTcQFvE2rp7fj2rhDNymgPCO5m7mkP9bwNR4xvjGThuaR6K0eLXdc41MCsSjp/S7nGN/ae+LRisyADv6N15SkcqIHa6aJYTHHT6Xlt1j8XHOMhtkhFkCYr1RUpIhCicQk1DTmK7TSrkfeC4ZElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwRgirMlKyM3DQ1advz1sstRkruHRrxEtjQrNQxq4/uy2RWCPXbcHPlunyCO63S3hTvmqQFtSjyHRspyQhJGdkBQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAQWhZPSMc2rjX9x3Tf9wVm6W3XpdQNa9FrLxztLw4ONG1M+OLlAZ5n0ZrAZnbgrN+/RA+wisQNxN1gmrHoTO9GVFCyCCI0+mPEHK2PfdKhP+o65UGd70/+DCVBgr/b4rLGcKI5lUECt7nulxLkadWfuIjgRxOPUwunu5Z0KK72/oFDtDef3eUguxBTaQWwO4Z5tLhSwHD8ky+btDYX/4nahaXmataGge/xfnc858XyMKkVHE1KMssIWqATqHVz7rjjdNP4ViotOGY+0Y+6evAr9GvA6QYpa+cZ+NyJO0n/7qtx8mYb/+B/kKWggXOQdkv5YSemxqRXCwV+NYJ4LW4YflHA6iH2c1psJWW+p26lYhHPQXwd7XpRT9g2okgsaFZNr1QlrfNx/qG7pRBYYAIvoq1Tj+rWQ+3q8ElHbXDcxLaSLJgRk13O0deztI5l8tN/134hG7ZkXM0fVKfyfZ6ausW1CB4z6Ql+vTrD9k9IFIUv9qHMK2xfZzhSV3ar9cajxK4nqOPJ/73vnJm/knl1bfW5iSZu4cJDq6hbU2bLNx6f2vA4LODEyWUak55wgFel/aJ1BQreaqYK/hmRPsvDz9CCERiKnqIS3zBsMafmjmtvjgyb4OzRElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwnmC0egssnuPLB4XCtvfHNXGqdw/xPf3kURrN9kQeTSV3UQJ5XhZnQs79BcZ8FbaF4GhfxspKbm1mAmxC0ku7Ag==" } ] } ], - "Wallet disconnectBlock should update balance hash and sequence for each block": [ + "Wallet shouldDecryptForAccount should return true for an account created at the header": [ { "value": { "version": 4, - "id": "6a5c48e9-63b0-4fee-ba88-1547466299cb", - "name": "a", - "spendingKey": "c0ae2e6716930fc6837ff659953d4416cdd0999add33c4a6bfb7b748c5d5f96d", - "viewKey": "a7c10ef904c8865218ade06e4d57697d6aa04ddc6a7bce052c14a559ac8ed8425e14852afa6c3ec155f082c50fa05f35c6b679a3b827e8d6f3e537c0affc39ef", - "incomingViewKey": "42594ce61502059464feb01764689c46fafcabdc62d178e0888b1f4fde333505", - "outgoingViewKey": "e250f39b02cb196bbbe57e381ab199ea21ae82e9dfaabd95f82aa6dcad713e5f", - "publicAddress": "135e42de661dbade17189fb1b52ebe7b73af3f00b0a22236468c3fcd5a60cc09", + "id": "22ad26f8-6324-4047-94f6-6320745e7664", + "name": "test", + "spendingKey": "5f65a8c9dc83cafa7c62a9831653e5c14131dae0c217cdc7409835145ceaae3b", + "viewKey": "666f5d9dcadaf6c5395eb11cdea1650879e36783dca34683635e33e6bfc543ce4413114f480e2abcbd0e0910d2fcc37a6b042129458d282dcc3477620c24eb01", + "incomingViewKey": "31b3d71788afbcccdf4e8b1aafaee039bfc41bc768c6d9dc04c1774f671e1106", + "outgoingViewKey": "2f1b93b172f76c3cd8db1e33fbdb4d09ad2f56e53b8756496201f8e69f94cc20", + "publicAddress": "3c0e7e7752ece79518668342353f58c0e2ded73c5f8ff59bab2dc934a650dc5d", "createdAt": { "hash": { "type": "Buffer", @@ -6604,7 +5390,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "7fb4be33957e2929393f676c52a61da3fd30f7d07cc9e324c63ff7beeeaf5f08" + "proofAuthorizingKey": "a09f3fc03ef8e59fc4b7f4e5c90f920ca113ccb6572761da439cbb0bbf6cce0d" }, "head": { "hash": { @@ -6614,16 +5400,44 @@ "sequence": 1 } }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:neId5as8nWnh1dkeNwfT60oMa8Eimkjep3sqjDz35Ew=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:xfYXQYm39V4hSkYFeMAWOI25Kw5iUTCY3bRCmb+6D88=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1717539004596, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAQAvre+kQkIaCW7kQ1/ZTGhm1BlX0yfJDob4ZNUEVx7OYjqwI9S9DEnnPsw4Ui6sbYbhQ7UK96yCy7MrcLvTogCAVEcpAxvhwSh6KN9aHglavTbp2dovF3LzFHVKjwM6W54QY1/guZlg9jsf9vJq2to2F6KIj0iupDgtVWZL5ZykMv1/4c5IhCAbFgxCbbmfwn0VHec7qsvoID+NTnxbozPPgDQuB7ymZrAafEj0zHLOuSpk6bIHUYHoTyINvdI82QcfNpGVI4j3bwyQoiHYoOWNjBsnUa4Fzv3L7uFAQ1djs8Z1eH8W1056B/xMzTf8MZUbA8s2DjrXdM4CSRubuihtUBdtzNyUXhCVDbEprbHLiehM4RAwbjqo2B8WcLPxxRsezfH54n+iIo60PEnjm1/eynKLAB2cBzJwUTnb3uzfSIb2M/lJ3S6RTXWKdIlZgKqdgTcEFTil9l8/5CV+kuLnKiUvvd0tzf3UxXIdgsyEsat27IRfRTRYAbhI0fWbIRUvy8x7dpWO2sfCEHIJrITqsk+bdztvywVrEeLkhewdJ6PrX+6j5M8Vr9Vu8iS5lLbSRUCqDwNTaTnRkASmqeTUcPzGiVQbMmt5UhSE+O/ail/Uh7rDqpklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwyocN2WaEeJlxznepvq5LKiCOcApaZIhLhZNlyTGgi4TQZpRqPbpTMB8/aTS7bD7ES+29kxkULU4fhbyekZinDA==" + } + ] + } + ], + "Wallet scan should scan until the chain head": [ { "value": { "version": 4, - "id": "e55bd8cf-aba0-40e4-927e-40be6949497c", - "name": "b", - "spendingKey": "bb3ba1a4e7c3bc528aaeab2633d92a8eca762d943aab5cbfb7b0d4caf2a6e517", - "viewKey": "894854302b48846111ee21d32cf2b84959586ac66f782f0ff907173145deca737982c369eb28891ecd91c2a11e319d4db020cc7091e6e715ef4b9dc6721d2723", - "incomingViewKey": "a986bd3bca479e5ec9a5a8f26a0b234b49d1ff9c5327b9917d57913d4bfcc503", - "outgoingViewKey": "eb4440e0bd5787b009ae6a71d0b2d7b493fdfe0d95d33be05e6e6dcd0da49eb2", - "publicAddress": "92056c5da34c774e45c9192f4d54cdc7b4e5bfc068897e49cffe47f797f0a543", + "id": "f0c05cc6-518e-4a60-8c0e-36b624461c02", + "name": "a", + "spendingKey": "cb469a5b96113d3ce511481ea95f5af796246c8210ee36ce933459f20f9ad054", + "viewKey": "4ae13a3528b39c2159b934eca7835ab728d2cb6cf053d103bd90e2c1bf8e1b6c3358415e2dd8955c9c35f33fff9888a0d39f7168ccab32b57b8443adf2d4d5a4", + "incomingViewKey": "ce8f3645d46a27302d7cf8f50b925fd6093efa5d0fb1281f2e05a6f299b07505", + "outgoingViewKey": "cc5eaab7ea9948a69faf90cde50994cf5229b3d1a4ed346009895e4089b3deeb", + "publicAddress": "58ad1cffd3236ef303e9543a48ea92784f46233e97de4f739abb432860bc7244", "createdAt": { "hash": { "type": "Buffer", @@ -6632,7 +5446,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "7fcdfc565d9816e4c386cdb82217885c7ae2e603033177bd4c91d7a9d85b860c" + "proofAuthorizingKey": "2a24fb483ca3e2f2d5e1dc91513da2b6026b6c89b92e0fa154e9d05d4fe56502" }, "head": { "hash": { @@ -6648,15 +5462,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:ckxG76qAzR/l+BL7Es+E/PfyYSHovhqtZl/Xcqcq9Vs=" + "data": "base64:/00uglzCYK1RtfBd+WM7QsY8fcMjUE+tPuHc9GBkgSc=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:vFQG9StojtP6rZwpVrufvNhCI7L3mJfz/dAOEqJTNdA=" + "data": "base64:y8WerlC5JWcBLf17AIqMoKxfYsNZvHczLdfal/RGwgg=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538871563, + "timestamp": 1717539009281, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -6664,25 +5478,25 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAb8ykFyFvqfsUq5qJSKhinnnhZq4RywzYBhvcwieC1MqDi7DmhWd5H06hln+Z9x37G5qrE7HJJNdid63HeOpGE+L6ZjotHVchkR0QHFoGl9igHFTFe5ubwCRjUBpT6QdFjIbUAWiCjx9SQaCHRokXhAEsE4s7f3T2xgu6U3EjZ/YCKgmdKUaMz7bkFopaUwMOIHXu6jo2Tf5DLHOP6DUYobrfLg6fRgmREwmaf/y01fWMFbWgixOBQkcheY9ZKNnSH3e+DekFb7hdKnwSkLQV1LeD4rEbtjPww+00fMU3csea8gqD7AycNObbUWKbpMKcD45cPvBUrIhOizW40wyll54QQSu3fDveTEng8CQ5vsOjBnxNN67sqWUaxVr5U8BXNvkijakskyhefQ7peFtjO+EXyUimd5xrxzmgzbf0bTYUyTO3rUmGgNXhkGvv5XQvm3NvSNhm8aqr50WPpzX/2770ebg0wuu8w5pBKoktB+stJy/3oJotk5WIlVQIR5ubKiyOsjBsIR74OCVSvAkvEtGbnb6G/IhCxwkPL2kmG47pKzlSl8aw+YKFFBAa019Q7pbznPz65qNAYw5RsGHdcSutjdHGrhl8Amrja5ciRAo+Uzo8N1d7uUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwOBCNIrHuv4VUgVjwKUZ22sRoe5B72Fhasp5jT2x07bLykW6rw+kRPGjThAfGucAB53oNPylWoSd08f2prZVPAg==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAUxDTstYMuM9cawjYTcco2Rm43dbO0mhx5TdArJFcC62j068X726p5jAwYoKZoZ67qvXlWAS71Ezaug9k199xVN7HiZbw8hIhr141W0DWZoSY2A4ck9/j53UHZ5irLzjOGtRLRg3hzUGfJcUNoBsxjS8CwAliIczo9xJUlhnCicIDlA2uHFPoYWUeAzX2uACvsSpb9zdjFdoAtcEXPTpj05VFac0duw3kjHmDMJm1/beCyZpPVZx69pjDzSAp/Qlp1WsH7TQxRGWGvdy/oYRXFsI0KtmPhVKtzPAyc78WNn3h3En5gGcnzr/LHT5DUnTeCputh8QgYsJ/PtCX+wy/Sb7L8UnZWkqkXBhe1HkRrOYdR0VjQuVztszS915xO/tKwTQJPQR/TZwAnQu4nRbcDyVZVPeUpOaWGqE2WBcoY9tNZ6vcsE5FMg8E2P3G1BCGaeArJOqbMOpNSdVskpXDqIWWQhN3vR0t5dFnu08CYF5fgnPK0Hv8BbswFQCjsoV3ciltsQ5KKOXHIuCs2kT7FwpDQnR9Hy5NySzk3QKoP9YZTqRmf06iCoxsqs77Rv+n0VlgQGCLAeeK+YFAyeUab+NNybVcVXSCkTA7y3AH2JFlTiL4EXGavUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYS+KKDPB3TsOXtkukoBjlMZZZ8q+QpNVTZz/leVM0NGVpTdh0RUsbf54yQbeeqDX7cFl4dY+lsZyR7uj7s7DBA==" } ] }, { "header": { "sequence": 3, - "previousBlockHash": "26B5A48FD98F6C8DC5FDD8FAEEF8142AE63AA8EE4FFBCDE6E0818F7692A848C8", + "previousBlockHash": "8B89B43F3956FC769F957449516569E48AAE76C7E1516F43781F37B40DDE6741", "noteCommitment": { "type": "Buffer", - "data": "base64:ARTqhia4P+mIapFqlSO2WMNTE9+TqldyOE0EdSNSXxM=" + "data": "base64:uo9tMuotmcfPO6KvlL6q4CnesDqEYlm7/TFnZpHWTFA=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:+Z1XAIlRfVvNS5Q4nVfMakJsXu8ibAGUluggK9h1W4E=" + "data": "base64:mzGsYnnneE49w7ACX3U8Fg0FStW0D3fmPnEhZXI84xw=" }, "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717538873930, + "timestamp": 1717539010301, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 5, "work": "0" @@ -6690,22 +5504,22 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAADeTqEiiS6zAtcGIUeFyXJ24W4aKQuMDbRhXeJ1s2GMSOtgmwruJLzr82q9bADT2qD5V3tPNngpeyqzdmB2ofbtbBIvg8ienVGrWidUBUrlCFSS6TCrspbpwJ2hIneojYKyq7Y24ZKe+YlmQAYNQoSy6Mw7kVNMgoR84/vEWJoMABKq/QKMgN8dyJJA22E62jGp52BeS7DBHNNl1Ap4Ppom1kN0QNNoA4rsJQbSbW/LuBFMHGHsX3LOgDa2qww5/arCXIe6b6wD14My6H1YjEFx0WwEElaEm+0u1U2vhI7OJbFKf7/uyG4aIn+g5LA7I4kMf72oG7gyewwAaAMEyCxKH/KLDafAn97PnEJ0xI9yBjr09pTXGMrB8tXzM9F0FQglhEhPOQtzNYiAcElPkDWG4zpeQpQIVAmClnzDjWw5Yh3D8r3TB4wZ9BiuYH3YrSPuA1pedV9QhUUYE8QMJSU842TSmRUkOJ7JJR1iNoDb1efTnjsaEjTcHYwWw5cm5SKmFkv77A1FGdvwuc8w9B/z08kLOblZERs4zMvVhFoJ6QwK6rj+/mvcnXFVpcmXxe5HKXzjZeJdl8hX09ghI8TfP4emeXFD1Zxi3hA90ubmiCpuEF2j5xEUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwbhDlC542gvg5F15aWnw2PLKX6mVnFxbkCDHEQg/l2KbveB57nBCFLBQgwOsXUksRdzEORk7GdY7AVJq+CbgeAQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAm6ILXOTB+khyWx3f4nNeMYILKANSUsv6Vf96Stg+qmaKLl90+O/8xwrViZfXoKbS4+pokN3N+6CWOVSEgGQ6FXkV/iaNvSnGHZD83/Fg1s6PQm3b6eRg8UBbAU+IQi0i8iOSlVro8M+XZ8ChJrAqFLvvVEhh/uobrCCRYYqrWgwAi17NqR0zbtGA/mLbW1z+2ngREYTnGEfrtjo0YK0rTlzPJkxF8bsQu5nn4I43XZmNJ64fmHr9lIWoed/wQ17ErBDexmCW48XxxXtHj1TXXyAdTZ8br+wXRuDA3Jae+KM5fgIRfoUIM4/bbzt5bcZRDxIGkygVw8PYAyjbhtq9nbA3lv1U9+hz6JFp8XI0ZxC40dH6wKEAMJRKwKGjeAlUphPSkVfDRJnHRmmIrAkPJxtKJKVQF1qTpz/DRzYacDQYfdr8abQ7QGwltMhuWeESYKd5B+UxwyL4zy6ew8MRq8fDEQSnnnVSWsYfrdGmoQcXgqh3oomq/rmEhgsemhwDJ/rnEeJ2MTESpc6n1xV3MunG9jNNMCBk8rzZwHMZmQg39j7mydsv90nlZJ5gaGM5k/SOAzekaECc79++dwkUiSebWeCMSb6e0fYS8rWQ/I1Dhn8sBYi/W0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBDKVr94wIo44rKdGWzeNvRGHkMwEMw3uAZV9HBE6mG+KJ30/UMOSL6QMU3GphsQH7P9h2ZSKhiVMXrikOgo7Bw==" } ] } ], - "Wallet disconnectBlock should update balance hash and sequence for each asset in each block": [ + "Wallet scan should start from null account head": [ { "value": { "version": 4, - "id": "049ba4f6-931a-44ea-aa5f-ef433ec90753", - "name": "a", - "spendingKey": "20e380868cfc0b2756eadff9402b3916341731118b077552cd9eec97c9c1c00a", - "viewKey": "7a4da4fbd1efa4d0afb8739778f43f690a1b310a49fb1c33f70d85ed443594602a00b0cc239cadebdc9c7860ba96a344c5b2911b01efe18ea9d75123fdc4d0a2", - "incomingViewKey": "082730efefa0ab9c386a1ed5bd0a064f42240aeca8e9a3fd5a5311d06750b204", - "outgoingViewKey": "26c142151e6a3c3c88f0e6c863722b32b76ae10f2e39d4bb13060e7baacdc5b9", - "publicAddress": "518cdb40b18edcc515f251ab82e56b1b33850a5a9ec837c81973bb2274f85654", + "id": "fa5fec21-c63c-498a-80bb-5919bee4f57c", + "name": "accountA", + "spendingKey": "2089eed9e71a6ca6ae3fe89417ca1f42349aad0b2ed0f1da13fc958d84032880", + "viewKey": "5dad30944afa1173644eafbd2e4938cd3228d3aaab8668587a2c0ab0cf42cf256ab4365696caeb17efe20b909935f90e5baf6d84b2d70f7fda4a15440aeeba5d", + "incomingViewKey": "ed5859e15f2ad48861e603e4b8fcd74cc22637a12e9a4e3ddd8a4c5c03908b07", + "outgoingViewKey": "6ae21c18dd5b081ecec4fe73fc417ea7c83213e218403b3b80a92d8f0420ab20", + "publicAddress": "b6a489a014a64d0113c110531b99e19927088f301bedd11034121c28ff977848", "createdAt": { "hash": { "type": "Buffer", @@ -6714,7 +5528,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "9f3a339fbf57675eda822fe6434f13822ce64a5d2d5d6884fc98c77615195402" + "proofAuthorizingKey": "8083d151507df2903b0e39ad090c3467a8035e6d230348638b5c8fa4a53e2607" }, "head": { "hash": { @@ -6730,15 +5544,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:s4Yi/MuKDM4qGHH1j/dzHXG6aXnE+fcZx7w72b1w8Do=" + "data": "base64:RpA3KWtFNM2ycx4RqUuxT/lTJXhNsgSSzA+s/MjUzFk=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:2ce5E7c03qw6ZPD37elDhbyzxZXSrNyCJsQ9Mmnezfs=" + "data": "base64:kC61YxPC+w4CJ48uIDdKrdlZvlYygzEdESlNqDfQj+k=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538878477, + "timestamp": 1717539012639, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -6746,82 +5560,76 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA+d6Bh3rNKCxPVVCuAf4ZKLZvmtE6tF//46sa4FYpxEWSOpzBFpq4UcwpHkLEC2GFWY5VC0hY6dSnG3/yPD/O7AeYeL/oxRBMcTIB4v2uuEO1cMebwoVpdAW8uDEkgTSC4rpTG2esj7qRv+ZLdc3whdcsGbSgzvAO5eFoeuosPQwWX+7Rbk3boL/Lo6qPpFBn78j9aI9rNI5YxNu7ZO0VFYQO1IDDcpKzMn8Lcrfi4uGR8Y/RsIF8DGO8sIG6SqKbMCM1ovrdR7BieNuUPeVpLJZb9ahRA/F32rrZXEuHoCy+fxZZYJqcs8JOXGwD31gCn0+g+yq8kBvjAtmzomY1u9UjxFBkddY0RY64QGJKexS5H6AGPuYUfYZOXZFdbc9ShHAqrcboyYvUr2+pbCCMrmpOuZd5lIEktGhTNfO5/7FrB9RBTMhrFopQkmCKNSg0YQ3bpRPLml+8ZE2YThLyrUtn4949dcqPA5O6oy7m7lZAUXCED8xvd/fUruPrmTWXh/Nnck1s0aebuj+jNt+qIHnCv2RuHNxVE4UhuGNgdzQD6AhrLMBQiZ6avbdswENIiyHcYP2q5jSjFzN/hhJch7U+zI2vxdiUmnta6pSUwkdnRIvU+IYzaElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwk5w+JVKihJUmq/7Uc27qM/oIgGSA6aZGBLLJXd2qyW+z3jjAjBlf+IrovzqLwsB423tlhOIGos/MXtR95IkbDA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAQtcKGcQLA9vdKYUbfiboDRG3yNKRFR+TYJ/xaVIdd1ezoqSGFv7an9yxIx+jZqggiPeT3jBvxXc5dkGCWtC4aBBgms+g6U1iPf747E8V8tuxabDVbG4ZUUH8z2J7UfO+Tkj85q3VZTik/LNZJMw4PqM8lNixWgPSf0mbNeJx9LgQGyfPwlFJi4r102u7pUTy6NrzD4fJUdMVJ57pI2sHs+luhZEiNy5G/PU/kHlTUGeqWGpWLyV0X8HcrmFL1yUnvDTqXI5wGVxlRL1LKxhYZ9/bjZgvzCQqJin+IYLc7u+HrcfezmDEmdPUsBnAi2Jsx6k7Wpk7C2NAmvFsTP1UTBVMQ7yvBFWuBr4oIF7X+iEQPm6IxXQhkeopc4V/UkFTbt7GOPPPxivTYQw3RGo2eExeyTosat7UvwzZ/OQxQNjSKCa+U3/G2isyvH+rUqDU5VUKTXgUN+BB/4Ev49nu4dSo3Xq4S4lEoGz7QBaLscCBrvTKnHU+GXpNO/KpgZmkMWV8P8mgDBLmmkeSg8ZvrHPOHwhT0+k5cdotIR8xJ0NfAmZ/J3v3tjct5axZZdm/dymwlxT9OjxbNZBWQfpgwghXg+KYILEMVMVDhhQJ7bmDT112vhoDzklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw2ggYFzS9r0pWoLNkQXMT911ZDG0P+EdOhVbSNjRSS0tFLid01pUX8beOMYMESLQDWCpl9awFqHDFqJGqWwlRAQ==" } ] }, { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGy967/fBpCLW9sv0oCvwmbsaQuFPDohRKI8di80SzN2zW5wURMHQj1vAeztAf1NKT4clMEFh1t6ecQfm4hWGn/rF4zn3Xt4m55JEkDjrnKuyh8UZQrhABH71w7i2B25nshb0bg+E62Omo+O0wFRaOy05griKdK3lETKCnGUQ5WANjKOvVRcbIu62Fk3AB+GwdhRuhCr4hvnzEmx/c0j/Hrtd3hX7pdZiJoMp4zyvOQOxp/XEaWLt5ZsELzOUyEvgVPEG252Kxrs4z6m1Y4yIl8GvDoeT0gLwIv3Uw60S8Sr9P6cb7Bi6+5Uy1IOdCIphOuQ8/kodbKzvelPDQAMbYJc3iPDOj6rxaN6rTjzZJZxXC+tGHFsuze66x6t6/+UyIIrLG0zWTgIfaEktINGshdWpxssVJZu58h57l/xOahzMil5OxpLfyD2OuFiHqPomiuuNbBL5AAQZPyJuQyJ7YLZkfJcuJ5j5SLlqQk6w2jeCBWcieBphWUFPSXpyML8coUyj3T/6AY970+VFx4gfXIzy55v6DFkUhZw0pSQeLbGw56IM1miMUaOWcZPER7NO/PwtEjJbvFhGMrDboLe3RMviTz1pHG9HvhqrJm8SQWoLzL6G4BOnT7kIpyfULZcUrqMYNhwWPk7fsDUkqU7b+BsQOBGJFlRKnE1ZkNodwSfJZdTKUoCM8gxCJlepOXmzPQxkozWqTK6kCg5JHEg1WAOi53M8sTtIgRAKVWxY+k6SHXaGzeEZm7l2VgwfrzJiLfKep1F6ZtoKpSN+dG2oTE1Zwfacyy8VmIzjlkVln955e4wNsJ7vaQ37i2gpB2QTGgpGu13A1NaThreUi7STsOWFIN5BmB95DQQfwpUL98uaWYhSqW/25iJ1+ew/TKM+vgP0MUvBGsJs7lmeFiHzznKQAxFqgN2fqkMXH/B9KisbRlnlpeUGe64G6u5tAHXo/8O05f+TUSHjtvEzu4NTcp7OlhQ3MDekUYzbQLGO3MUV8lGrguVrGzOFClqeyDfIGXO7InT4VlRmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAF8utaQNXTxJwjztt8ATwGNp6cVIG3oVpIzivdvv9L8PEu4gOAPAzhitVZqKE1Q61g9hly65kMvGTYp2sqjpZQrBPJCYPyxhXSG9gcEohagom2FmbDIcV27bgO/KY2X5a4wraxrmKzIYFi3J1DqDLkX1RSdQcG6bSvp3IsKjqwsE" - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "B2570284AC115B3A9CFAA38EF8F03F6B8997B59655DBE0EF2CAA9DC2F05B0BB4", - "noteCommitment": { - "type": "Buffer", - "data": "base64:Xz5s56uEpF5d/BpUbB+6qmZ5+7a6G3UXQpPKyQBslmY=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:L61kuPw1B5sAoRz1Y0bWK4YU1WwvfrtKSynuFbuRiE0=" + "value": { + "version": 4, + "id": "b348877f-52c4-4880-8a4c-1c41f458c46e", + "name": "accountB", + "spendingKey": "fd92f9b2fe73330502288db549743d676172927667cf615226256bbd6f6a2a3d", + "viewKey": "dc745ff1c68bb7ed051719541823aca83421d6a55f23818aaa692b0c26b85a0bc0931f4addc90bd6b9c8c36e3a6e792c7b6c13b2312ef35ba95cb54c73846eee", + "incomingViewKey": "217269c4aa780e41dd102d3bd6dbf3a192a637b5cebb4ca730be5b8bbd5aa403", + "outgoingViewKey": "8e6945c8a702223641536e38de45e76cd64ef9e8ce64f2f901ce7ae8ca6e8288", + "publicAddress": "b85eb15dcc56677ff073dea9ebf79d284f6f34a83466dab734cb3f5e3afb53b6", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 }, - "target": "9260366780148527510972123832573278885902566341756516011968728852484845", - "randomness": "0", - "timestamp": 1717538884282, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 6, - "work": "0" + "scanningEnabled": true, + "proofAuthorizingKey": "46af1b8daf03a4f4aee94a987e6d590bc7ffa7cb6781ee82b5bae126e53c6006" }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAGzkoYW2EUxZoYAo7xCqsuB45+79IKlj8x1uQ6PQmrsSFhk1yaPVI1H5LtK6tkJ4EDKFVHoyELL+ZBtQsE8SBWU05MCihwSV7f3DWaS0ticeZzDRjCWm+6MsOcCba0V31lDiiv3J5HBAYqlSqlclJAxLIoGtwROS8bBRMcidfVvwY+XEnIeZCNmjWGGb2zVwJrLUCa11Uk7EKXPxEver8UOVGb8qY7wO6oNWnQwFdCl648kqWmeXCmXYAp2zbpstLpUzA+oCRyaJe0ROeAbTYzTNkzkgZ/5A0YZNkbQm9tf3my+uHgUA6DC9GF4eZT8UZcSCnNrkc+IBc8Nq084AzNT7NkGl3X2aW/4CrVxzFZXvk8xSamzehf5QL6B/mdfMU9FlzZ7hsi12RBaCUjyxX7MMSnPhcTIZvfUVpiHdAFp46qzJMeQXoAuokH+M+hwPntAxirLePWVaTZ+zFYTWaSjwdQf937+PFjPlLA+E+ZlprknN4va68G9Uzh0h5/iUyjZdK0AcFrLMAJxnIv5JxF3BdJSAVwS1cV9w9mh2RMk98xB/PCiEDonVdvsKdolPAHvhokpwRpVkktAQzJh93wfIf3oUT6xx88+3a4ku1L+d+x4PK1h5v10lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw8utdH0ZHQwkpSjjtvVFWCz9fx7VrMp2Ely4hmsA5ZkP1u6qkyThgMmwDXGbuOY+HA7lszscsamAm/Pnp6Yv1CA==" - }, - { + "head": { + "hash": { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGy967/fBpCLW9sv0oCvwmbsaQuFPDohRKI8di80SzN2zW5wURMHQj1vAeztAf1NKT4clMEFh1t6ecQfm4hWGn/rF4zn3Xt4m55JEkDjrnKuyh8UZQrhABH71w7i2B25nshb0bg+E62Omo+O0wFRaOy05griKdK3lETKCnGUQ5WANjKOvVRcbIu62Fk3AB+GwdhRuhCr4hvnzEmx/c0j/Hrtd3hX7pdZiJoMp4zyvOQOxp/XEaWLt5ZsELzOUyEvgVPEG252Kxrs4z6m1Y4yIl8GvDoeT0gLwIv3Uw60S8Sr9P6cb7Bi6+5Uy1IOdCIphOuQ8/kodbKzvelPDQAMbYJc3iPDOj6rxaN6rTjzZJZxXC+tGHFsuze66x6t6/+UyIIrLG0zWTgIfaEktINGshdWpxssVJZu58h57l/xOahzMil5OxpLfyD2OuFiHqPomiuuNbBL5AAQZPyJuQyJ7YLZkfJcuJ5j5SLlqQk6w2jeCBWcieBphWUFPSXpyML8coUyj3T/6AY970+VFx4gfXIzy55v6DFkUhZw0pSQeLbGw56IM1miMUaOWcZPER7NO/PwtEjJbvFhGMrDboLe3RMviTz1pHG9HvhqrJm8SQWoLzL6G4BOnT7kIpyfULZcUrqMYNhwWPk7fsDUkqU7b+BsQOBGJFlRKnE1ZkNodwSfJZdTKUoCM8gxCJlepOXmzPQxkozWqTK6kCg5JHEg1WAOi53M8sTtIgRAKVWxY+k6SHXaGzeEZm7l2VgwfrzJiLfKep1F6ZtoKpSN+dG2oTE1Zwfacyy8VmIzjlkVln955e4wNsJ7vaQ37i2gpB2QTGgpGu13A1NaThreUi7STsOWFIN5BmB95DQQfwpUL98uaWYhSqW/25iJ1+ew/TKM+vgP0MUvBGsJs7lmeFiHzznKQAxFqgN2fqkMXH/B9KisbRlnlpeUGe64G6u5tAHXo/8O05f+TUSHjtvEzu4NTcp7OlhQ3MDekUYzbQLGO3MUV8lGrguVrGzOFClqeyDfIGXO7InT4VlRmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAF8utaQNXTxJwjztt8ATwGNp6cVIG3oVpIzivdvv9L8PEu4gOAPAzhitVZqKE1Q61g9hly65kMvGTYp2sqjpZQrBPJCYPyxhXSG9gcEohagom2FmbDIcV27bgO/KY2X5a4wraxrmKzIYFi3J1DqDLkX1RSdQcG6bSvp3IsKjqwsE" - } - ] + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } }, { "header": { - "sequence": 4, - "previousBlockHash": "C0BDF738AD62CDCF238CC8CA39B8BCEF0FEC0B0ED663C7A48C34A0F7C3D4398C", + "sequence": 3, + "previousBlockHash": "09FFBB8D53A173E1A982C8ECB57ACC9651732D0BBE0BCFF971F19B097F9E72FA", "noteCommitment": { "type": "Buffer", - "data": "base64:sivJwFVBBCDl41P4R3jX9tvWsP5y6f3y6lPa56RzWms=" + "data": "base64:KP/vitIP8dVAxPlWs3MzTfd7PzSY2sKpfA2IIjVDkh0=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:x7Xae19m0YrW0vFyKxOPS5E6H133rkrVIqc6ldiUnl0=" + "data": "base64:RKvoN7dEaA18p2zAP9UGIMXNfnvpA1Wdnycb0NaXCPo=" }, - "target": "9233318228143625020618577701423519925017621426082203201059080050516648", + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717538886454, + "timestamp": 1717539013985, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 7, + "noteSize": 5, "work": "0" }, "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAt9ZAf2K52BMpV6vLbiOPzv4vQATwf3NbDNIGK/tqW7OGX2Yupt4uTqg4hkIEXVAnRvjgTrMVA1jiQtN1lndRD1GlHBgdbW3Fi+hPsiDiutiJa4jNNqW52gFG3kOaEMQ9Vg7blTAmC7QAE2at7FOodSNwOntfZK90MICARSJEdyUSzu1Ux6Q1W53yT2S1jElaFM2dZsK27XfzyqEvVOFIVXOSNUtRH3VkYD5mNV/1ZImKoNK1WOXPOdIGz2JMGOW/U95dF+vLwP/ER86oKepca4A8wHoCdulq4QON4IJ5VotUmiGqASRhr+3DT0eoEtnfJXEQLf8OPeFvObkHFdDMwb4MiNmLlWLiGofkaRSzOI+MY40d0/DWJDTI2Vi9IF4OD/avim1M61x2G9x8OgWn7x+K/yGrojF64oNSYV+sFFKDBiVWPZC9z/hSBW1/XBkLqlACtTUeYr1qEjMduaKSKVbXa4T/W6ImVnPpmxmcpXgZFBMHFGy80wgBun0REor05tDCDunbBYWQqObBNfUga5lM9IPyfZGHY2iybo5YHtErrpkyrZT/VI2IfnsSUf6kXK9Hb8P3LCiqYS9PsD2/VsRN2xYYOc8p47oPWo4F5HT05r5TyfAD7Ulyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwQHLFya9LuojFQffCrOPp02iOHhG9Y5aW2uaFx0Gm04Yzm8H0uC9YBzK/SF4+JAVsnY8d7zR0bLPzGoahh6saCw==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAeRsNEYt3ZwfhpUmTnlAoxMI6+BpLof6oRfHanFWaLQqWZCetZjOwdBadG2PWNRfhOYCj5kCz/FYXyqKDMKkTU/gS5tSiLixEqeVWt+LwUneKKoyXAeZ1OKNH/obHr4wFzvBwbPZ1/NhhgsGvbcBmENXpKynPgC4At7PCqBjLn6MS+xh8L8JI2YI25c2Jzac7boKEO4SUJWqQjnhT/PVNx2NJkd+ET9pPxDPxVJ9qaEGN5lmieKU0h91OfnpxUPWoHvbHt0fyfrEXLJOH2IMF1P6q8YPzmBNIPE9uWSh1Khrcv2cKbkul0cpw3Nt32rShV5kTakqHe/LipBTzKGmRSyfMk3R1qvkdot4OrlSGrgB9MMJKLf9yjvMe2Mq3p0tRzViHcNTZm4LWDwbaG6MdKaHz94M4iDqzrLa4lxGQaNoV6qLzRvvUtvliIlUh6ZRyXS37gNlD7Q7BNWuGEMsDmjy4xt0/fNsbO14ZaTKnn+EPbGECCOoDKX1yDFBlhzCAOEd41+Mi1AT641saSnvO5sMHoLy2r4x+lrDE7qyCL/3SClcmj0PFbi+CjPhSJn40hErDnV3//Q+X5tw+NcSBzwydSpdZgwwRm38Y+h3u+IsG281wffNTR0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw2eSVCgULZgJi31KzK3GdX62J/O4YGGwndBTL55y1Qd0m4Jq3ikdnPKJCLa67MczlYI9hwYzENBQbt3O3NOeRBw==" } ] } ], - "Wallet disconnectBlock should skip disconnecting for accounts with scanningEnabled set to false": [ + "Wallet scan should update balance hash and sequence for each block": [ { "value": { "version": 4, - "id": "cea4db72-3c0e-4490-ba85-0a19f1b2af42", + "id": "825e4a80-f3fc-4244-a3e1-dce2193fcd55", "name": "a", - "spendingKey": "d0ec738764e95ace955f1d7b432b4f130efcf2049b0c2354bcb852cd8e226d6c", - "viewKey": "ed77109abffb7d9e35de05a3d4d4a8958f639943c3e487871ab4e7b41c5c9911aa7269bc46ab31c43f036a9f6198fa86d59b4bd21022319f1975da8ee6d32a22", - "incomingViewKey": "46eab4105e43d0021db1fd1fe3140e7e23e951efae0c6c13d130282b1e3ec000", - "outgoingViewKey": "b788c45b68200423b608cf34888daeb2f56938e76c8d0297a572e43c989655f5", - "publicAddress": "95205789be04b75526c4c33241dc8a94add1e0ac4455a66786bf53c84952cc3e", + "spendingKey": "58eb718331c282d7fd759fee0a3ebd0196ae918bb2bc2085da041f41aed207a2", + "viewKey": "ab85cfb9a1ee4922b24d909d6c0e495d4f2d742c3a0afc872c488a836d551371a29325b2f86e28a87c0f425c44eeaacd2acbbb4e88f3ef3a35b47567e4b6388f", + "incomingViewKey": "7d8dd3ea50047ad4447a9916de0f34bc03927712d8a617ba2c3012b33989e906", + "outgoingViewKey": "b20629e4e0a199ad01436d5ed504b0e912a3b7dfe64f21bd2ba5b0b099cdd220", + "publicAddress": "14e8a4486b79f64937f8dd84d23afa5980728d1a9f63d546c55d96b3755d605c", "createdAt": { "hash": { "type": "Buffer", @@ -6830,7 +5638,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "6ae14be9b436bfecba83f6fa2bb91a9de744591bb43f41746e5aa965690c8605" + "proofAuthorizingKey": "b3b33adb285f558e666e66d61d70f04c1440f1de96692d8edc8b4e564b9a0805" }, "head": { "hash": { @@ -6843,13 +5651,13 @@ { "value": { "version": 4, - "id": "72031132-15e3-436b-bc2c-ee915c5b454b", + "id": "ae195ec3-2434-499a-ba5e-1e7525fc67cc", "name": "b", - "spendingKey": "5261a189da5e08d5797137e520abbec9b401603e58a597263ceae9b5c3a0b042", - "viewKey": "773d68c98f3e4197c379b784213098c0dc5eeba9cd2396d884c37176826d73221a504ade9945c0b6e03ee0e65f77b05447085e713a434e3a23f46bf415995b0d", - "incomingViewKey": "c8933e4fc82fb3d0b145ad385e9db66ac6e6fcbf5ae274991eb5c11226ae0f07", - "outgoingViewKey": "92af0cd3d4f015a0c6bfb0caa6ebf667a2851436b7445fece8766cca68a0bd78", - "publicAddress": "c703444078753dffeba8416cd0b152f9317bd190474ee6206328d166ba79cc9b", + "spendingKey": "81465f82a3e863de768db3f3df447c88fd66a9fcd74d7b832080a81b8dfebf29", + "viewKey": "27f155cd9d320b59881f4ec9101e10f3ea45ac361ae12213d3f92cfe2fb13e6fa40f057a9d1059c1fc20fd16bc34ae3571aecb36bd70fb762296e83143931c8e", + "incomingViewKey": "f03820937c6de19e91747b494159535ec3385451436faccfd8dfd9f8c7859401", + "outgoingViewKey": "ed0fde53f1dbf38d1b40e983ebde9b8ee6536153885e772b055d4bba7f3accba", + "publicAddress": "671f81804898b2f7002bb07a27b76b73fba95f0b0bc134a5ae4325f8bf8fb633", "createdAt": { "hash": { "type": "Buffer", @@ -6858,7 +5666,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "2700d803eb6def99a93347f9b798e663c4420ca874a559582c24d868bfa4fa00" + "proofAuthorizingKey": "cf173acbf8aaa3e24f6ebc7c15cf0f9d59c6048e503083bfa1b8ce232f44c50a" }, "head": { "hash": { @@ -6874,15 +5682,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:4Pm7/rxUpCE6r1IMblGXJTOhuJmrjwRA9OOv+r1faSk=" + "data": "base64:EPBO4Pb0coY6KJ9Vv+xO1izVPFOemeKI6im+oqxOr1s=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:D5TmiDwxrHVWaxxwPp0bOs88tEbBbkYASoGLXC5d7Ac=" + "data": "base64:SruRXSo5H/JY9OIhb23x420ryCwvMoBRZ/5VkHgTkt4=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538908054, + "timestamp": 1718998708731, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -6890,25 +5698,25 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAlUK4uq++YpXh5AxTydB7Y4bEJe/mmVapHPB4BAL0CoK21Q7MpGTPBCm0U5jAbHFCPB7ADfLgaV34rqKCefaqfbT9UBSAr7uyfcVz5k78CvSwNSAG061Wi0r6J77dA8jfk85O94SKd/AEpB3xoPSWxuEkM4qLbh6Prfg4e8d+haUTeroaP1Lb4XiIJxugJl0qBDpiBZFJp8FcPW8hMlm6l2S4plniz93UhmYkdrATyWuS3kOwxB80J/zIqgKMCYnGRSuE6LSzc+UN8+F/8DoMiRLECFZodh688aKUDa911YitHlfGWwzNP7euvSZQLIdtfH+tqCMf+dUO6Lnk2m846fQ1kRDMKAVTz0TT9BLbaIbWVT9Io6cEwn26Q2Yy8u0nkIXcYFTZSvP9SN02gT7b2Vb1A1hkNXfOGozjASmway5O6zznsxk8CVUqQGVfH5HwOREl+RqCrcduephTUIwupXPkwcIC31qKGYIa8sqBTnc9GbDA3yocaOdSYRcuDBFBYr/ofZ4MWJiJJXIMEMZNVPx4l/aoZ8rVX4zDcia1Z6hOej60KYeKsc407IwCt6NZcp2FROpIEtVVOn6G5Af4Esb+KW8x4PsJcGh1dt/L3RjQ8tEB1J40hklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwmvC5vkg8ztfVFGvQ4mtMVsoIy70lGzD54s2+0kEzUrUJBFBvjx3tlk4XyyvGPkXqbwwi8wkbMGkPkSBAKZYaBw==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAeEasFfctBRZ04NSNOOpYZNWtKYKZ4w61b3ESf47GUfOxEgHgijAycu3lld2Z9tLc8Z6RIzUjJQG/AjAWtWHI9AFXiSkviCNrH5TKGDvEhJuS8aUTDPlUfytEmGkdl8yhOcchAoKpA/hlZg9Vl3f4+QPcnON7rSX3lkpVhtpnzJsDoj1fxFThkleGQ73DQdkB/oPA6lSP3iGJGh8regrjiIz7FwWDyxjkODuDE5MRiMu2SUoAcpjZcJ2Eo8wBqWL2l38PcMhKOpbvD4xD1aZbJbCX4x7/SHeWLkFbshdetUU5Xo3p4e7eNbCS20poGMVzN2GH3oj0y4+1DWSq7VvMLQr2LKMWkPWjPs0o+dzN28EOhywyytVOsvfnYhsp8mduf4EdUUhpQSBPVAOCr13yyidRhmWevoPIkTKr3KqrdtGZ8F1DGE/5A164rk41rMH8uT9CuwnL6LAdPHLBjAuwKzeaqY10y23clN7uCKvzWx+UsxsR+aGWbT+R856WE8y2eWCAbz7Jh4jO/qAXpgpLtLIdRrTt46ZkRjwt3ALDpMQbINh9Sv+vRBNOb/rJVSBBE938xSp3coMV2JLEO6Ll44C1EEaBPFrFusU5CoY9jguyj8igwleSfklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJi14SmKiVWsrsTo7VD5PJ2mj7WFP15n2jV916tGxx9MA7Y8mLVaOhWbSuoygEy97RUlNgGX97py2OndJs2DFDQ==" } ] }, { "header": { "sequence": 3, - "previousBlockHash": "18CBE685174195063E79E28674202485FFB63ED6F47B3388F9566FFDE5CA1EFD", + "previousBlockHash": "B5539191CBF67B06B628EE57C0E75FBDF89950E9610558B37C34BDE4C3A7B433", "noteCommitment": { "type": "Buffer", - "data": "base64:ogJoHxeBjrLVpdDXTwUon7gP/v5ODhfYI5vOQceOJUc=" + "data": "base64:TKfwwMYhHPL86FtdOkBeKPx2G9uct6rI2VZfSOw4WyI=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:1ZVDi5SxN4jhWm5e9/YnuNkCiGqr/ySypdNkzmwolHU=" + "data": "base64:83FGXGFrEJmYij5di0lZO/429lDYKHNl98YT4d7lp38=" }, "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717538910047, + "timestamp": 1718998709182, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 5, "work": "0" @@ -6916,22 +5724,22 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAgJAYr7G9B4yiJ80g1Sg0aghZxGcyDMMzKqmulsKZd5usbNt59K48GDCfhYgR3doj+5WcPF2n5Zoa/73oScN0lmDLsuz4eTPKNdUDepU9TgGP/dDI2UpeGODBeRii3mfHhdVOsMTRwfE0pzk3Bnx5ICtIFcBwhQNT9RRJbILfwEsCdx4Jfwyd3eEoWE9YLHcJtcFIqwbMnvJAgKhbRmug0kEiHdt1IRJ6byv8teGpxHexpEagw+8GraJAxbHqGgzCSmEOlKEZCyrFg7OZQpUO1+DDyTQXqoqrXyTLn1yhZeyYYLz7kgSB2YX2h/PKMCmI3P3R/ffQ1byeFitRVSU40d3vLElWt/ewXfibIL31u6pvN4MEdW4hvLTwMfU/QHAdLTh4ZN2idnLiukKIfC3CySgL4xK5TVcd8ftyfJGC9dAdAXkYYhNQuwFrvUoB6tS4+/rBnpqFnAZ/kEk77iorm7jEY/ibvUiUqCxAkfr+D+jqCA8Pvbk6JQc/9O5AoUc9EG4+bgnccCZcdHkBgXI9viPNqbBtu8lEm7YQUDgxtzQvanTBsIpR4xG2VQQCZ+2H6CY1Xzvh2etJ4v4jRvZnnM09DHChD/TbpFXFvBYc3utRl3SWbFCHjUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw2AM7OHElJs2qtKyQmxYHsmSd4JQBOoKKV/nhIFVH6cb7Is/6Kxuq3xUgg8ZJznXGc7+jMM2krHDSm74GQRfqBQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA72ClAMJ9fcgPPhZOTzW+gBJUYlDxpH7FdOWkAB9Rj76pQvLwaK2yX3NO8RYmdwE33xJoL0rbuJ/ElbaXOTbDVdXzCThjcnKUs0L+1Ks3YCW3SoMdNGZ/2g+18hH16nDA2uCVu2gnSWeNNT06FdxxkNgUjY+JqAPE2KX8obpHYpgQ95+tFdX4qJ1Xsa5q2v0DATHgfqEWZAuv73BX5jXlPK0z+fARRXxUlvEhnSwEuBKruu8JNrW1qSozJO6j/ZnAxzpFhW9AaH2EFD3J9McN6X39H+KS097+CjzLA8qzy5YsKNBokORChRI6byvo3kc3hEx9HhIx5a2fdMfdGWs3jbzOi+1O4WiBLMUyq//BPlqv8YCyG7ZJGVaB2FTR6A83LJ4wacpsIKdmY0ZJkFrrFIhKdznDNaMFSUvR6aNzA1Twonhw8YhNq0U3RbMMbVEYnXPCVyU9DfA980b/KHFkGtjorxdZSpfkjMvPzmWpVXHEHMPTrWT0CdP32QVxdjU1pd9FUYYQnufXVtpzH3kF9qP5lIa//v4Xa+d+tlDBv1ZJIkP2JfhCSF4obHuq4IArE1gqRH23t50XiseJNx99IJtacP/KHY+oF2uOrngJGuGMIzmG3W/v3Elyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwor/BSgv1V4DhZv1kjV+3mBf6+i4HSOSrj6blyhHQO7JoN83/EBbj+/U1RKM/TmPJsyn4yWu6saUfo8Kc0kukAA==" } ] } ], - "Wallet resetAccount should create a new account with the same keys but different id": [ + "Wallet scan should update balance hash and sequence for each asset in each block": [ { "value": { "version": 4, - "id": "f53847de-227a-481b-a812-d7d3bc470f79", + "id": "65e0e2a3-b6f7-4244-b081-4dc218c09a68", "name": "a", - "spendingKey": "8b98a67d7682196a381b5d60a2620f2c91e5b73a2b2e3f7fa820df66658799c4", - "viewKey": "20775b11a44869e536657449c5855cd0915b71a7113bf49c743606420414273852c0c2aa8c2750a118e1b8a6a2203df3b344438b7c2ed1485385ebbec84742cc", - "incomingViewKey": "6109c2999c98a2e918b72c61e86da7f0e0abe9149bdcc850bc27a1eb42f44407", - "outgoingViewKey": "1f5613cf9e3adca3828dbc5d240fb945f8737681782080eb49a4f7e5a97a144f", - "publicAddress": "9219c0cded1632973b6f30fe89a1ef5d2ee497a43a9e8b4f327ad5e2c74bdd09", + "spendingKey": "4d79a56e084d8a0d2cea281dd016a5778188e6261777d680c75d61193678e45c", + "viewKey": "3d958c1c0fa717d3f7aab984994430f65567c758572a91447c74b1e119b1c3e00163509e747a47e11b9235b611502bea61adf296f7ddd2a70d60f4fb90937732", + "incomingViewKey": "f68ef0ae034a1d451e3b0865297008c2ab42ba3935efc844c6ae45c87c542505", + "outgoingViewKey": "661cb216ea8f7e48fe14352cc795c1e6ace047a0c96cd0ce45ab9116d9812657", + "publicAddress": "e508e5c2ffe1c3b175047408199665151183cea699d68e00780ce4a30a023214", "createdAt": { "hash": { "type": "Buffer", @@ -6940,7 +5748,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "363be292208a39201f35e92ca187ce0c4cd09dff07b555edc2b1af6d38158705" + "proofAuthorizingKey": "d5f3cb5409dfce37fb857e9c039826a0520b398f755558d5e5645d2b03830203" }, "head": { "hash": { @@ -6949,79 +5757,105 @@ }, "sequence": 1 } - } - ], - "Wallet resetAccount should set the reset account balance to 0": [ + }, { - "value": { - "version": 4, - "id": "6a9ff94a-d3f6-4fb9-89f4-0ba9f707c04a", - "name": "a", - "spendingKey": "937a478517b57e3edac6b5f362910c7333d95fcc58c6fa400dac30b05441e57d", - "viewKey": "b443df461a21c2f783a01d9735720aee1df503aedbfdaf8c7166f49b2f2e801b96e721162122300197db9d96e5399154b732711c15d06b76ae231d2fbaa989d8", - "incomingViewKey": "e19764232ec43709b83322ddb76d7a1cba8276194e97bdb6ccf0c457aa19d200", - "outgoingViewKey": "3dbc7a26ca52b85339ae4dfa94e90394a15e162081d537657b565c1a65554a57", - "publicAddress": "b4230d53076043fec8aa43688879cfa6a3286f6a5f3ac64719cf9fa33744d206", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:4NS7yLFXJ7lejwrSwGvf2GWI3+1I4aqq8ZpDbytqvl8=" }, - "scanningEnabled": true, - "proofAuthorizingKey": "b623eb9b8d6578c459eea70e0e562faa59576cdaba377cb66bc6e49e2ad06b0b" - }, - "head": { - "hash": { + "transactionCommitment": { "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + "data": "base64:le3k1kL/1Ikpp38S/WYk6ma7da/Ia/c25RjBV7vmcyE=" }, - "sequence": 1 - } - } - ], - "Wallet resetAccount should set the reset account head to null": [ + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1718998710216, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAuxQW7dJNUZQiCBlTjmyYaI7bhlo50XhgS7BW36kT/7WkA5dSrsBHZXtVfvAh9yW8gh0SPe4OVZeelHSG+vgRwC3B1EzqlcFJ/wg69h/jtoqAIf0oGubE/TtCliw6aeQV06LXBdavxkIEGTnns7gArKSwB5c6eDXrJz5bSmNQp6sLA0RnkNz9QjRON20LUkCjo3EVi6/9DmQaDpVofip1tl/Kb1UgUWnLfbHu100KFa+GITlCwzoeKJCR5B4g84g4bDN9Z7PZurX+HbvqLJgNncJtpsgzuU6x7zcyqgwJvkm6Qn5pjz7IOu2txRkrzKY0IgOFzbFqHqa16jStcP7bPP9WU01bl/JMdqIeeGM8PRCaIOrvGS/PPd3pNsLzrqVi0CRu75T1I1kpAhskMXM6TV8LJ9OSjIJ8ZSsWZGqRz41UJlRJCjNpO7hJKvWSatYZf1pRMZPo4IXAZpc0X3dGFqyQJnDL1crRjeZPRKfeLnegTz4HwaY2Zd7ewKLfJ9T3us7P5ZUE4YiI8cfZrTPhePfDTQUWtmhGUHchXbsJMDuZ5HZKaCOlYr/4qWH1vEAG9/By3TggQB8UDuKRHyilW9UDy7dMGqiOwEUUIp6To012ir24eBudiUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwyRjJG/EXJ5fUz1Cs4gZS1EAN4MSIWrYkR7VlWBLEDGcpHW5tcFRgftSvFl3VSHdRm2CsdKdy2wbYbMFulQYdCA==" + } + ] + }, { - "value": { - "version": 4, - "id": "4eec859d-9c89-4908-a23c-4cded4b294c2", - "name": "a", - "spendingKey": "361eaae46d35035ae781a36b9e340258fd5afc2c9d3ebb24a80bd62303578b87", - "viewKey": "42208315b537e8d625a3c2ae5b77185c6b2cee1552233c7edd1a367ddfb39a54ccbde3760a337020c7ed42a0e3c75911ce6a10eab5de52551a445170db68f507", - "incomingViewKey": "169016b8098a97c18599273861c150e2232c7ea5bc93189d0eb076405920e807", - "outgoingViewKey": "ea8bb7fb76575ec9e5c3f5ab3ed106882d99b9b7fcc9afc224e0ce595e3f52f2", - "publicAddress": "8336fb0657aa4cd82cfb5fb4b6723300af8d8cd64f86b14ece8b0230c6c807a4", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsSkluerLUWvC5zECd9H09NAGcrk72CjBgCVe6mHWU5OQnohpcUVmSSrXizsbx4UGJIHBmWQhNNisr+Fj2isdMMZrLsSckU8H0ZRXFuZJSW6Zx6zbF9a3M9QPeQiqkSPRdXJgEtjNNyrqZshYTlhsAYHd+vQryTQemDmvX4B6qjQTu495Qn9OyZkVv1Tx1YvygkDcO1xWjZvQp8RtFskMOLcfcwtnO6455RjcoHJPCXSlfqks2A1bXvzurDUQb6er+HdBomaeEJWQ3SALOeiFVOdjcU2KGonw3/bBIG9QhCBGNuwtfOMCpbqoaE0vdGX56WvuBfO9TUgadZsGQuuFQlT7RTYkIuUyHGoAew3cLBu41Xj4vgQQumOdewasZ38ezI280bPh3ySbGJxlW6sQeAiZYrN/OdQtjwoG186EWmcebNhlyNRyXCm3St+914OfruZsO44jU4ODdog3QGShdI1c+ZkYph/xXxOQGvUHm5yTD6T7mkm7YUpmnL8yLsVjpZi7MWlrvgPiP5T8TqZRedEa5EFw4zb8uYx3Az5QoWhaNH+SdlGhduayYeohi6M5Hn9l+KNWWb3yzC2yvDsrGp1A8j4W/jqfcLRdBIj7Fnsemhm/1nqhsqgLFfeGgHC1H7MVOvw+EFjBLYpwJ51vSbSAj6e1rSGIMLCOWNZkAPrGAvBRp6UWpfwTqQY0af2eWbZxMTCrncyaaZizys3Ef73yfggzpjLvltLEPfcbFXQAJG/1G0BCzX6xK0ZJsk6nK33Oc6T5We5dy1ODLnqpYo9ROPdpFuSosxnFFu5UEsby6CnpSOCqHSKSnyH9MfUYR8VQ/hvM2G9RqA46ugWELPovEfJS74aHDyQqfz4CPJsEuh9RBlMBLhuWX5ODVpO5g9f+/YfIQWQ/Bq7fg0FocA7WHotA2GgajPoMWuoYLp61URHOwn+cwyOgtbNz7RdzXAnCHrvIjuQRQurSkafvLwnR8zXeoqJr5Qjlwv/hw7F1BHQIGZZlFRGDzqaZ1o4AeAzkowoCMhRmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKAAAAAAAAAHsPTkV4veuMKoWfqTn4c40zztpYF2Ld0MxjnYcvp//rktg3o/nfF6Xzl+mY5MG9OvlbcNHHTu4vIY+TJChUFwB1SxZ1/fsleNYb7n8UXIYSwdFyz9o2C3VdVvMp+fUbYn1P1Uz+TS9uTm5Uyu8RRvPEz40Ez53X0awGvBp6uUAC" + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "272FB9B5AAB050F9784343C1FE626C0F666E5A7418C06FE76D6E1166CEA75E1B", + "noteCommitment": { + "type": "Buffer", + "data": "base64:R+qEOUj5s/kM3zyG7IlcXqeaUi1E09iK4bgC/82opgg=" }, - "scanningEnabled": true, - "proofAuthorizingKey": "42a275b3cb8c60e9b90cc9e4d8ae72487cd3af1eff1dd61ec1b2cda588ce460b" + "transactionCommitment": { + "type": "Buffer", + "data": "base64:zQTJ9s7FJAAJmeU/gcdiOYHWpf7ororQ7fWEMOrwPvk=" + }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1718998711370, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" }, - "head": { - "hash": { + "transactions": [ + { "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAlHkmFjj+49T6eBDK+xP0DK8MOchyOu6SPtFQsLqPPQ+qIlz5nFS/B566IT7doYmiq9nRR09JcFnONh/BuoAF0Bs+uBzwA0Fp4OXUUvAvTciyHavRyYHy3D2Htd69GwnAIAFwq6HnvIbmajV5Xf7QcgvSL7ozSpNPTCdWoIRGkaAVnkXsqi18QGCJl+3fLtrqLOIGzf0W40g25EpfXTsT32JLu4dYaCWhXtXxnvXqiK+CBGSxQiOqAMs9Dm45VFfn2NS6mj+EX4vsKEgtrajseDHpOiI/aFsqQTbQNUCT87YNJeW7jPk310pg0kU6oHrYdnyjwcmQLm7jGAoCCaBWzmXats9rAYNtunXfxG26bS7ruz6HQvkSKmmMi5aWxcNHSdb3BDrM1Z4Slav4TdcmVHVkxl0cJObA4cghRFmQys18CQKKi5AxCMso/puqGUtIET4FcFqsN9Fw+seV+8jVbqhcqSSgtYkehyU/qjJj0vm6C3UnAnZus/ViabS+PIxH53ogLBLqtUeLdV1SFiy0A3QJgoAzyLdG2Xm1UxnV8hn67lQmRQS6ahd7LFlzNC3tEYvao1sIN5+ac7c2syoOqoot7L3XcOQtJ35Dz0EVOfnmIprc+0SFWklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwkqGM4g5fA4H/ZjvQBZvYEFHTo/KKyT8JwgK1AJ7SUlHFhjgk8tMpgUqgLb/y1lSewRzpUZU1lINhcL6HWsGXBQ==" }, - "sequence": 1 - } + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsSkluerLUWvC5zECd9H09NAGcrk72CjBgCVe6mHWU5OQnohpcUVmSSrXizsbx4UGJIHBmWQhNNisr+Fj2isdMMZrLsSckU8H0ZRXFuZJSW6Zx6zbF9a3M9QPeQiqkSPRdXJgEtjNNyrqZshYTlhsAYHd+vQryTQemDmvX4B6qjQTu495Qn9OyZkVv1Tx1YvygkDcO1xWjZvQp8RtFskMOLcfcwtnO6455RjcoHJPCXSlfqks2A1bXvzurDUQb6er+HdBomaeEJWQ3SALOeiFVOdjcU2KGonw3/bBIG9QhCBGNuwtfOMCpbqoaE0vdGX56WvuBfO9TUgadZsGQuuFQlT7RTYkIuUyHGoAew3cLBu41Xj4vgQQumOdewasZ38ezI280bPh3ySbGJxlW6sQeAiZYrN/OdQtjwoG186EWmcebNhlyNRyXCm3St+914OfruZsO44jU4ODdog3QGShdI1c+ZkYph/xXxOQGvUHm5yTD6T7mkm7YUpmnL8yLsVjpZi7MWlrvgPiP5T8TqZRedEa5EFw4zb8uYx3Az5QoWhaNH+SdlGhduayYeohi6M5Hn9l+KNWWb3yzC2yvDsrGp1A8j4W/jqfcLRdBIj7Fnsemhm/1nqhsqgLFfeGgHC1H7MVOvw+EFjBLYpwJ51vSbSAj6e1rSGIMLCOWNZkAPrGAvBRp6UWpfwTqQY0af2eWbZxMTCrncyaaZizys3Ef73yfggzpjLvltLEPfcbFXQAJG/1G0BCzX6xK0ZJsk6nK33Oc6T5We5dy1ODLnqpYo9ROPdpFuSosxnFFu5UEsby6CnpSOCqHSKSnyH9MfUYR8VQ/hvM2G9RqA46ugWELPovEfJS74aHDyQqfz4CPJsEuh9RBlMBLhuWX5ODVpO5g9f+/YfIQWQ/Bq7fg0FocA7WHotA2GgajPoMWuoYLp61URHOwn+cwyOgtbNz7RdzXAnCHrvIjuQRQurSkafvLwnR8zXeoqJr5Qjlwv/hw7F1BHQIGZZlFRGDzqaZ1o4AeAzkowoCMhRmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMKAAAAAAAAAHsPTkV4veuMKoWfqTn4c40zztpYF2Ld0MxjnYcvp//rktg3o/nfF6Xzl+mY5MG9OvlbcNHHTu4vIY+TJChUFwB1SxZ1/fsleNYb7n8UXIYSwdFyz9o2C3VdVvMp+fUbYn1P1Uz+TS9uTm5Uyu8RRvPEz40Ez53X0awGvBp6uUAC" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "93DC731FEC65C2E24E97378DEDD55D439FCBE79E56CC5C6944161F44FB109E40", + "noteCommitment": { + "type": "Buffer", + "data": "base64:kBHF3YNxOL8IilNDokogvegDjm/I9lyfjlDm6JvSNGQ=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:Bv2NFa1AlF5PoTAOmrvgJMKq9fJl13iSdftNIS4tdZ0=" + }, + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", + "randomness": "0", + "timestamp": 1718998711826, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 7, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAAPFFBmW8j/PyVUgfNGgfykCVdiLfUUWcO6tUmbAUbFmvENyjwQlIydjPxm/dxavuyPCiYqLtRVgKVBRBaN3MCGxAqHeqmTHDgdUrBhYFW0CSzgOw+p1ptuixYO7OF3vt6HEuHsDNhlMEPnwVNaOUfPDJYgtf8S5dMHz8AIGYNCgOZmhaRhK1yjEAzyORc+cafbEOyD4jPlFLffaN6wCOjyVj+egevkeWjFwnpXqzyp2CwNdwfvPXvWsDgfRiOioMLkSOuIwjVWTdkFzVDVP3T2CyP2II2ShS9HNYSeTsI/YBU4zSDLGdNM+x2EVGYD0NghsodJCwEz3sxVt83eNTjNKvg40G2+h2rN+U382OvyKN9/ZP4xAWELu/XOmzf39zgR0J0dOxnV8XX2JlH6tQt1Pyuen9V7wPoqIsGIoD9JPb19PIIFluJOg7t47oxHopHpWiTLHhm3jZfz0WdurvZIptXU35MkXWEemxGbOMLqUwxt/SPhU47JbYLWBpNyyUHfBl/GlXoAJ5fYLKZDxnnz+UGyGGFSRq3/q+kYHWgbNMD7VLSdr78x+jDiaIBNucfeDsXV+Z/j5ZTudoWEZn+hsfnFDxvHDOfJ7kd5alq8NXHJIr2gbOnklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZ86XEuVDtOd4ACDILit2J+sMsZUUNrNCfU8To3kNidcl297v8EvP6TcPpGnCAYlekcuysdQy/Gy70aaou0KmBg==" + } + ] } ], - "Wallet resetAccount should mark the account for deletion": [ + "Wallet scan should save assets from the chain the account does not own": [ { "value": { "version": 4, - "id": "1db0d7c0-5dc0-4af1-9349-6dfdd1eec19a", + "id": "bdc181a6-6b55-49dc-9e59-c0902bf5b8ec", "name": "a", - "spendingKey": "d380f4af79c40738657e0ee8007a4759f9486df02022273d48e2fa9cd6576563", - "viewKey": "7d408b8858c58099450f0ea14ad0e810064ff08a73078617753eb29d30b7854aea22a9f5ed8612759086087d7b78b3aa50913829f494820a533ce5f13ddc7291", - "incomingViewKey": "f16815402bf70f6e6ef3b167a3a940419db3d05f8b6eb56cff9d09373bef4900", - "outgoingViewKey": "92a38016572d0afc6a1c7bb92632134c761b4ef0dc82e6083b61c8ffd5a7c1cd", - "publicAddress": "2b617eadeb40c7e6679212318210dd34de44ae591cbb1245a80563bade2e9706", + "spendingKey": "7d48d21dad81529168478dd42c385abad164a8b745b2313dc767ddb76d994948", + "viewKey": "fdae9c1c296edfb134e49d1aa46c64d05417a7c940a8436ebed2a2b761701edc0f8d0bbe124ad2d7f6a6232dbe403b48505378f18f66ebf428378304a443966c", + "incomingViewKey": "1b5f8e55cc7fe97298fb0c30ed94d2a639fcd79c4c174fcd5051f490ebf8a605", + "outgoingViewKey": "bdfc222fbcd687066795224ccc27d04fb698c9caa56d7c87bba012ab7c907cd6", + "publicAddress": "c253b29c18945be96e99e6787635cb8111d144fad1e4bf06f4224dd4deb0b54e", "createdAt": { "hash": { "type": "Buffer", @@ -7030,7 +5864,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "c35690ea9aedc88905087aee7b51edb296caa78169107d016a7c2e785676bc0b" + "proofAuthorizingKey": "cbc742cb6fdb5dabd8e1c61e202845fd518dcdf371dc9d97911c86ad60b88a07" }, "head": { "hash": { @@ -7039,19 +5873,17 @@ }, "sequence": 1 } - } - ], - "Wallet resetAccount should optionally set createdAt to null": [ + }, { "value": { "version": 4, - "id": "7beffcb8-b04c-4563-a719-631d6e742547", - "name": "a", - "spendingKey": "d02703f5b2ce1d0e9fec295b9ebdc26abf840553564c7c6c82741f7f85be236f", - "viewKey": "8dfcd122af2b54d80db4593bb9c01c01a4b2dd0552907dd93c6bee790429905e18d941069116640a12c94d619f770d43b2267174b23ff5557d57c396aad28b9d", - "incomingViewKey": "f35802b507f06f6c9f0bbeb9875da6d290b147a0586c7cb83e341c120a005c04", - "outgoingViewKey": "0f418c817a72226b81e89cd2bc1166bb593d6469aadfb758301a5972a99f849e", - "publicAddress": "98648a14194cd07bc1fa21d34bdb15fd41d556250dcc10f35888f24191cab41c", + "id": "e4a46dd4-2963-4a3f-8f8e-21743f7e36ca", + "name": "b", + "spendingKey": "f3dc9f070a757849476e8b6a7c81e5877641de307db7c78f71ee4d4c22f24844", + "viewKey": "35dcdd6a8a7862e03ef133e1b1f028864644d41a57963b5698c6ce3d6f842d6e8e099be4cc1e5dba687563ff94cd1c7bbdab80f6d4c96b598ea694992d6b796d", + "incomingViewKey": "4fa528a002b17148e058ac6853e546e1a8c5fcc36d1dfee7d226bb3e14aa8204", + "outgoingViewKey": "8363fa72f466c437fa86902e36b035c63505128eee67b0626fd9ef95374d647e", + "publicAddress": "b0ce6cca74144339b1a1f4e75569d937a51e00cbc1487857564ddf40d4082f07", "createdAt": { "hash": { "type": "Buffer", @@ -7060,7 +5892,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "6f473605fc3fab9a66ea0f715ad6aeb36d624045086bd6c442497af0d9771103" + "proofAuthorizingKey": "1edf1b1ef2e6d68c9d6f79a7aebef28a66023ae210d609d5019588289a5d0b0a" }, "head": { "hash": { @@ -7076,15 +5908,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:xxBl7uwenihtMyNth1+es8eijtbg0APwhjU5JCkEXmI=" + "data": "base64:0oCyhTiqAZnyKvyvwDkJ1fXFKQ/UwC+XSWLFvm7B208=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:0AZsQZxjyOjkBfy5wcV0LvCB407iMYL/C86Rx7pxIgY=" + "data": "base64:dfExbCGnuMJybrBuH6nRpd4+oXyRPDJyDmpnrla812M=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538924255, + "timestamp": 1718998712927, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -7092,136 +5924,90 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAU3NULCyyfGTLdHPRcjrQu6xvlLDHRFNKQjpVRtnVe0aFKPWfudzbpib8u+yazaCbxvmBHvvnU3Tf8vEF00ny33Lgs1Y6bIA54mCbacBeT12recZHGrkqG/KW3ddUYwMxWb4J8XEaJcpEjX6ZjHsJUTz6nAtRA2K339Q7GJSQpvYRrzBTxwpfsrmoerV3Amn1BJACOJDGfAqQSqfLcNY8gPDB612FWTDZfq/hBk64WeCKyLL53ZuzHjQlpwRhqpT4Bmj3h4W3RoSFE6dEA+16ns1bdWkdG6nPWWqYYpHRlJziwkq0n4TBICEn+yrxWCJkWhAXjm/EKCndP7v2N8Qr8SH7dmBmcYSGmuEwRmN18CJJjim0xJAh157fy/mSRCJnc/FBr9plEGUmcA72m0XCiDOXtuFmOsWHoIk1siOcWFBA31MMKzsst+IrnOxX245T/co+fWCTOfWCXXQrmxAdB+iAtPmzgmO8SAlA06DJmDmtBeUVHb2vsXIXZDJjsM/jnFwV5c2vD9VeN30jf2WGwdZlOsdZbXj/59kWhruEWdWZ3XthLQqRki0fLoYGwn43HQblYP9RvwipJWmR6a6nj6ryB5DiGJ0NKWZ0lSlMJ4mJ7lw6WGUuHElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwEbvDPp+RDW8PbuCORTKWYb3bJycg1pljPBwWGbYnqt1iumQhbXVkBooC4EvqlNL2gNOd/iaBJ4FStugrQKLkAw==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAuT/alo07JnoPN5dRhQh+6/w06bPgI5p4PHdHqUm1fPON32EEVBtLg70eV25lfx6f94FMf0sZfh65dC87WqwbxeFw2BThu93vb7Qm28V5UMawD5iCI0u5PyuFasE2A0R8yBaPWKk8vhGwO9DPZRZ3qsZq9d5M+9/5/o/iXlSpK80X+rZsRmGTrZrX3cu8FZlIjpwg3oW4dWMEyyMjNPcjEOrLkX1d2E1wPFszJhp8vIekEEsB/xb14OFfZ5GYUHha2wBGHedlbM0e3goFlM+o4ixqjR2zYb+2gWNS7H2EvTez5t5DMvqaBBKsR1iv4tHqDohyk2roorG2Egqn2twwZglAvpdbWxviA0XCCW4WXACCYHKyWwjo59DR1h81KaNbkONqxfD0+EIUZAJFOQmUjdbWdBP3ncjVbIDlTnfJsYQSUwJBGXiPmAA8cj7oJ8c0hXRKEn9REWOiXvhrsW7BMeM7T8mO+MY31aY6LNrty8r35+7pwOnHrmjEgxqnYErMZUgWIsRTzuQlmd4tmyIMAzw6wfQ6ySXM5gztmAiHqHswZb49KYOlLP8PK2cGbnBWC5L8M+F4vikKLab9o0WLuIj6aweCVT4Mj3dGoOh+StT7sDDQd8imlElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw5ao6b2Po5V/WyZJsLpEEy/MMw5pvWW9Zan47u1OtIGr++SK2gVFrJ27EDS3kM015GsavU7sjg6oFk4TojxG3BA==" } ] }, { - "value": { - "version": 4, - "id": "7d4d70b2-6ec7-4167-9151-f1438c7b091f", - "name": "b", - "spendingKey": "dfeb5befd86eeec4a28484a98ae0945bef3d08d66820f23d641248d455dd8a6c", - "viewKey": "ea99d3ba32e2afdf7868ec05b672a611c7027fa1a0c2eaf0e46cc8eca047f14b8ed9d2b0980196608ff44f698b8b4f01f626a79bf764fc64d705d8d0293dd236", - "incomingViewKey": "3b7294d5f761708107d3de2fe1988055dd54c68e4718af20d7fe1acfd7532702", - "outgoingViewKey": "a9186f68b889911d78461e9b5576aae0b2d97547ba4fe809b36b53a53651ad70", - "publicAddress": "bccc050a0c7444743fb01d59230dde81003021d6cea4d8b239afe021102463e7", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:mZq6o8uyv4LYoSbNLuuK+fF4tYM65yBRkmI4JuPAnok=" - }, - "sequence": 2 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "43394e75285fa9d2c1785fe2758c26820966f0e0d38803ce0b21d8cb406efd08" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:mZq6o8uyv4LYoSbNLuuK+fF4tYM65yBRkmI4JuPAnok=" - }, - "sequence": 2 - } - } - ], - "Wallet resetAccount should optionally set scanningEnabled to true": [ + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAr3z206a5rUhzKJ1PC9ZwCsJnyl3xxi+OL6vd8grPLEmpgTz8GQna5Xx/xRnmVgE3FGd2BtpUiQfwzYp945ryzkeztzFZQAWlRdKyrqsUsqanHd55LDnZEPf7mKJkTRSsgQd1xmqd+ZZ0XZUowFaoXj0hQsqXaDOD8mjxsa++ex8LKVT6frtt02xtFbtr2qJ/olVS1OpwpqyLlcgRTlUP1/T/0eDOVAUyQAMrLBC/8dKOxfQ345n00/bC4W+EZfqnp+9OumCTjFyjuq9WN88vuXlI9AUA+z5T64MDGPcs2eRb98zQTS3k0AfxvRocHeOQ5ddTMnn57y+9v9/MPoEZx/QDwSAdkas7/Qcf2DdHhjrGhTjmSgpbSmbg4obPfDYmvzWaNQL/QZ/3P1XL5bU7Cvv0k3JSr/1w4s6fvUAA84Rlnz6Yn1jS1iqe87CxIUtJQ1wwcxR1Sg/RmmrRWvObgKZmCE88eqtQCbhvU1FKUU4TwkctlbOnEG+MbN9CeC2Z4i0epyIw96oUhkclo1CocjKlemvpIIoLvVlMN/k31hPzO/9gxkW1HxUeuQQVwSZqEx0GjUCMRL91JtDiwMjJWsIDk7ys3z3/uVmpXxiUQ3hxDLjTmOKYiqaOcGd+e+40tYKrbuKpC9eg4tPR0mWXR75bcRFL2WIx++sXvm0RFZPgLxGirUqCB8VmQJ9OsKRvk07fB11aZkRkutNkV1fYZHJi9zd/nmyrrjWVRxQa5NMY65ZqrVPBow8u81PRrreRhVQR6Js9ci2A7hWkp3t1v8P+FIsBKF+ct7OFHEuMXJgN65m2gzJ8P0xbHtTu6yTmfS5V4YfVUX1uR2VCVVKONQEjf8EcKeZDD7ga1NeUbaoMuXBn200Cc+DranuFsGeVR5Ht6eKnLC3gzlGjuX/XnfOlp12X8b4pjefe3neg01A4RsuaqqlmO4of5rTKLaT9tOG0yZ86tiv+R6EhuCeky/hq2DgSiZoKwlOynBiUW+lumeZ4djXLgRHRRPrR5L8G9CJN1N6wtU5mYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoKAAAAAAAAACEmPOiEeDtHhHphVmloBA6u+Vqp3QdND7mmOCfuem9MLaQJtzH5bGRy0xAJlk92F0pSjwBj/Lz/IOvISnV/PwlcO2Sq3MirhgCNadmJeWO5gABO/gDELQX1DfIyemNgby79UkS8swG68VnulnCMAQewcKbHLPbuhOPKlbfFv80C" + }, { - "value": { - "version": 4, - "id": "c71a1138-638e-44fe-8058-6f61c4837c63", - "name": "a", - "spendingKey": "ecce415425e647d632f625effdb1717428b14a35a8c29bf1d8961b028be538a4", - "viewKey": "f9af8ad35f04c95bdd6b84694755bd5dc5cd92345f8262a33558632f491e145970f01a3d9e095287f2f3bc8f1a020799007eb914e439842918857737d89d8a6f", - "incomingViewKey": "368140c29ca137998fedb7e6e51f20411db9dd56a796764d9e361590ed23de07", - "outgoingViewKey": "acd19c3801777aa4fd97cf7819d08d3bece3d14b83245fe29b24acec1bb4fe96", - "publicAddress": "81ac0d338d2048bac791a3cfb92818638d183465c3fc20d24dccd73e0e442d8d", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "eebe1953bc0241b2e20885684b0c533e110ee701b2efd97afea2ef00c999360e" - }, - "head": { - "hash": { + "header": { + "sequence": 3, + "previousBlockHash": "A1DFD858C000115E3B6DD56D93625B25E839A4416DC12A53C0D46C9B627398B9", + "noteCommitment": { "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + "data": "base64:28uBqeZXKceWrBSOAt3/AosjWSybUxDbN2DivVo1/00=" }, - "sequence": 1 - } - } - ], - "Wallet getTransactionType should return miner type for minersFee transactions": [ - { - "value": { - "version": 4, - "id": "c56fbe5c-2319-43c6-9c74-6957bd458e80", - "name": "a", - "spendingKey": "e64dd4e579c36542e7bb22de61769f11119a7449f2a28a48ce1461d8cfe4ea29", - "viewKey": "c9f799ff86541fec1f0d242c4f48056292ee077d739b5ac24a5a2d9636bc7d0f05522b614e5300c3bfcdb64f81d19390ece894886a632128503c66b644d4c452", - "incomingViewKey": "514c3812e86bf59e651f35cdf5797b41d1c3dafa1d9f3a6005781b18ac9ecb06", - "outgoingViewKey": "702f217dc84ba67dfa4bc09a3c8f6491514c9b5b1b05891ec4b9bc762ff9d4db", - "publicAddress": "60f29d4d089cbefe816d8892bc7a9e39b14541f4e8ab805819b3381ea3cd3d01", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 + "transactionCommitment": { + "type": "Buffer", + "data": "base64:eTSIjSVeiLWU6KZ4TYOaoeashqqY6Grbml/nvZBuOfI=" }, - "scanningEnabled": true, - "proofAuthorizingKey": "03aec8ded54e30e5422791974a8fc3ff5f0b9ec7ae7ff71b599962a024983508" + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1718998714070, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" }, - "head": { - "hash": { + "transactions": [ + { "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA/NJnmbh919a25sPLqkCkNxs+97nPuRPu54dBxfk+pV+vYZO48n2zfdtPqwsBBR3zd8d6USGF152cERr7hBwryUM5JbRCm3CJ0Rw2ohacwAqNnm7CuRly7T5PSxUreOXsTuDZOC0dv2Lz9wrnpCxkjBc/XffUV9EkT9QVoo0lfgENz3kih+rZeC/BvqIflJ0N4G839gmsVvonA6jzxChoNl705MkhyKZlQFTs4mNSF5ahwJMUKwj0EJRZWU3PQwJX8VKeHo+ZLyzPuhVd8H5PmJnSnJnsU92L+x12wRrU/yNd1oWOkOoYlC4Jvd4MUgK3K7tUI1VSM7LaNzB7XAvEgFMm8guqltnaVEQRizkv54Gr6l6xcUs1kg//VlhWKgxPJgpgAnwmA/7LDAr+zbZp3nupMUXKUuH7oR4b9SSao1n/WgzM7hbGBvcMxKSOceKZi02tPWRVcrjQ5APtFeF14P1/U9qeZKEk3MMaU0v2RC1Cp61oWidypjxxy2hWuHj2xiECFsVj3hG8gzSuVqU6K3WNEPt4C3fvazy1JAW13BCBdyBMQg6l48mix6joQJ2HwJNgTcg6c7dpkoSaMkqwzXpSTJRVFQKDjl8OHf0k9r6pq9bBXdp3HElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwR0LObUeOt1vE1Miyq610+3dTq9KFagXjt3ieQZG9uu5cHAvDvl+DfkSchQ18KQVNcnvF6i0SCgkd2rH/eGVLAQ==" }, - "sequence": 1 - } + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAr3z206a5rUhzKJ1PC9ZwCsJnyl3xxi+OL6vd8grPLEmpgTz8GQna5Xx/xRnmVgE3FGd2BtpUiQfwzYp945ryzkeztzFZQAWlRdKyrqsUsqanHd55LDnZEPf7mKJkTRSsgQd1xmqd+ZZ0XZUowFaoXj0hQsqXaDOD8mjxsa++ex8LKVT6frtt02xtFbtr2qJ/olVS1OpwpqyLlcgRTlUP1/T/0eDOVAUyQAMrLBC/8dKOxfQ345n00/bC4W+EZfqnp+9OumCTjFyjuq9WN88vuXlI9AUA+z5T64MDGPcs2eRb98zQTS3k0AfxvRocHeOQ5ddTMnn57y+9v9/MPoEZx/QDwSAdkas7/Qcf2DdHhjrGhTjmSgpbSmbg4obPfDYmvzWaNQL/QZ/3P1XL5bU7Cvv0k3JSr/1w4s6fvUAA84Rlnz6Yn1jS1iqe87CxIUtJQ1wwcxR1Sg/RmmrRWvObgKZmCE88eqtQCbhvU1FKUU4TwkctlbOnEG+MbN9CeC2Z4i0epyIw96oUhkclo1CocjKlemvpIIoLvVlMN/k31hPzO/9gxkW1HxUeuQQVwSZqEx0GjUCMRL91JtDiwMjJWsIDk7ys3z3/uVmpXxiUQ3hxDLjTmOKYiqaOcGd+e+40tYKrbuKpC9eg4tPR0mWXR75bcRFL2WIx++sXvm0RFZPgLxGirUqCB8VmQJ9OsKRvk07fB11aZkRkutNkV1fYZHJi9zd/nmyrrjWVRxQa5NMY65ZqrVPBow8u81PRrreRhVQR6Js9ci2A7hWkp3t1v8P+FIsBKF+ct7OFHEuMXJgN65m2gzJ8P0xbHtTu6yTmfS5V4YfVUX1uR2VCVVKONQEjf8EcKeZDD7ga1NeUbaoMuXBn200Cc+DranuFsGeVR5Ht6eKnLC3gzlGjuX/XnfOlp12X8b4pjefe3neg01A4RsuaqqlmO4of5rTKLaT9tOG0yZ86tiv+R6EhuCeky/hq2DgSiZoKwlOynBiUW+lumeZ4djXLgRHRRPrR5L8G9CJN1N6wtU5mYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoKAAAAAAAAACEmPOiEeDtHhHphVmloBA6u+Vqp3QdND7mmOCfuem9MLaQJtzH5bGRy0xAJlk92F0pSjwBj/Lz/IOvISnV/PwlcO2Sq3MirhgCNadmJeWO5gABO/gDELQX1DfIyemNgby79UkS8swG68VnulnCMAQewcKbHLPbuhOPKlbfFv80C" + } + ] + }, + { + "type": "Buffer", + "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsf46DKX34ev/n8SRi3cxGBCmV2/IZhZ7XFSVuadJmoyH+dz/UEq8ZAOggG2ih6PggKl1waEF6bmeDVt9HmA99K3mpNftYD/U9HeO4ZluPimmFn/JS0m6rNMy9Da7eQf8QrGUET+zkkcDxl+kLX2s/IC8qgG/k3GriLSSw7Hy22wAqroYfAyiiB/QBHyKIoeceRxO8o6UU27c5iq9qB+Z3aFylmal4o7aMLsgA1D5tGmqTK7NBUzy9le+QozrTcrq2VyROuYMPxDKgO6hsR8QmYLSGsz1QaEZQ4IBis7Bbv4WG+LohLJgy00cTfmcaRMQw5+ygwn6dwyvY/8XhKAU0tvLganmVynHlqwUjgLd/wKLI1ksm1MQ2zdg4r1aNf9NBgAAAHGMXWlmiez+Bt1uJnIJLiYxY75a4tnUcqQ7anXQpUo15bN0ZEAMRZOSTTBZjIEEoPovmmDVoNT2i17iXRdml9qR9cdNtaghFFWApvDNVbVTHzjCiekU/wSVIvn5/1WpDJlT465OqqW2d7607ZaByNbsGKoIWL7H+GWT9C9bAk6jWzK9Y23HvEEpUlDR04tCg4qiNmyZ5NPr/ww58X+hR32NukYgjUdONxFIYFpqcJlnia8adu07ERfdoG17V9hqghj6zr5MlRUbD0skMFr5JDgy/Sv0GaXpSXC1619uBtmUqhyW7ulf7SKrN8VDAIn6UajiIF26Jl1uJxVLfVoVxwg5k8VnYVCaR8P2Cm11a2dhyYvSXLANoc3Crui4xCI+8HFV4eB6KXQp01hpJ6dNA628zct/6pFP5tS2EphjGZPcgW9WVM1xesV3lkFDebI055w5f/tQZjRhRtDkqFezDErDRBIXuUCDsivEah+l6ccwrgO51y46NxBgWYrC7A3GOP1p25WpZkMEEnV/GZ2LSeQGyPy/8FKzgHo+gLVDyvpKO7uePvbWs3vcc2xzlO7qzrzOWokNe8VSHYFvGiFLKkN0bVtg92lpeWuhWNjiPSMczkGKzwd3/6XdP0WPczanQlJbKNOeoEV5ZRP+SPXv+cx7jz0u73OoLroaGs1ndFPgQCfzRfUf62FUxFVd0U0aFMnSRQtu3K+W1QmIh08LfdPWABakAm2AiUA9uzYat6O7E6UqnzFX6uwGesCiUnTUCV1TdJrrS7fE57DlExcy80P3KXaU7k/tZSA6Elgx5hd+bXlcZRunI1Kn8xb0d+oPUTX7dOYj4xE6j9MishxxPBzIHD7C170tiBEvKDobGSZHOsPcAho8uvqUXcvAN22xJXc6pyfWH1+HMvoRJHtA6mEg9T2ORyBlILJEdoIBgyTPE3E/yqd27lYAY0PJHg8JqLPiL15ZihxhXfvBcn3Pa2TPXD09vEawKjxgVSb8TcNUjPgUcdIcxnCHnsLf2aVdwErvutecZ/gwXEJqI5ubkXCDDTRDRX+rK8+HQVptrtdffzrGCFftztxGrDK4IiWSaOVWjOC9hRJfDBZQ7TP/VDstweDNPMTzEkfgfNIyrmDRUxA93G8NGcxOf/8GdaVYRJ87aZYZKSsEG2YuxbtwJPz3fb8nkQ+BKf+x4Knfo2TjLP5Xz4EOyCoWEWiNyv0MaTYGBQByNQzdDkPRAPDFuSw4vrGHLRunbYkTF3o4b1NLIn5/uK7GUHE1o4ENf4/bgZAzCEzMwI1yMEDexp4s15BzPWGwuJG5o6c//vSC90DjKUKFE6caTEGn7IWLTFmpkV2RzSPJQuV46rMLTQmh9J0RKTqikl1zS+DAixGec371JUy0cxvEGPVYfIAgCZEWtIRkcyJZHMjph/3mtzuLfkatu1YyMYwiO9M7EOgEy/QPt1j6wL6VN84zEG61bOpNGoxe/kxnp3GCneESt6rnKZ5v+GGGLORd9pZAjr6XCXgL7y+OhyEq8ns4zUyPsSl0384S25HGgJ5zHfmBO3aNrgBvtMnIFNMFXE1ybHGup4SNsQQHe7YSdoBbbD6QwSHxDQ==" }, { "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "sequence": 4, + "previousBlockHash": "7785D93CF60D2D01E7438966BCB020416F6FCD85CE6598C7AAEA3DB91D11ED36", "noteCommitment": { "type": "Buffer", - "data": "base64:D+WvBzZCzglfbki2aW7CExBQTYwhthCDqqGeTJNA3AE=" + "data": "base64:SvlE/JLGCc3Dc9aof+eSJoAXgQaRjYTRwnaar87sFBA=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:tZDwrof0mSVCzaFO/8KbpJjC4UzZlApxhdkikVYRcMc=" + "data": "base64:2/p55iiSZWuGQrCipruiSF4TzxZR843SFsl5A/gIvEk=" }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", "randomness": "0", - "timestamp": 1717538940787, + "timestamp": 1718998716388, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, + "noteSize": 9, "work": "0" }, "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAlzbVRjPC61/JquWjwWyxyuF8Aaxg9sW3d6vGFmVfHF2tIu4Fml71ATpmI0i6X92Fx9irMPI2CIh+UFgO2PmR6wsYv2gWaT6XQURcSGiO+NmB0zXFRDq4boO5uuSRfRwitcRjKhO+b3uqlLLOWzz61l1qjHH/Qpj+vio90vrOQgcCwMjDNE1P8fOhIQmPOhf46oixUERd/zPqEECvaVpml+4R2lYCwJJoYKvqqP1FL5+1ufTRdMr6YQ3g3KuJJSQBCepqWzFiTInca95LI5LUVfYsnkj7o5pQjG9FmeSDWCtTC2S2qInyEMFZDCGmbINwUoeROqz36qIvLPkdwnEs66gFaV6Ki85sVtxBNS53Z2gJN5SVLKqsoBhaEdY8EpkwjbgQmEs+lRsAIr2H/BnTpAr+JGTEQKTpyS9UK8jIOV2SHDAr54ogOLdQU/t6e8rFmvaMupO/zMktpLWXU5Wx/B9JFvKUTY/M3nmdhDmoFvzI1QGZklfTPzhvOqLVZmmZyOEZc8yL6ciwWG6g98V5Bw2l4dJvsziVSZskXhqLdHmmv9zqH0ER0I4PDYA2kOvbgRAZeoddLkWBGNbJgSWhOM8AqkaxanwG3NaCCkFLx2Y9pwjSz0YUiklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw+4Y2WOP5osklh8+xp33c4C6rsgG4d5R+Qkd4DAI/eICJfe4OsYSH7BQSSMt9KcOdZcT1/zgYQNkooCuo8LewAg==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAjPFV3XQn55MP/1zlKrJa2F7ArSZpTb63+NrkKiV0OGeTl0Ky1k01lcAqaz+ZRo7RTSSjyUXDWy442D6UclW3Wuv+yK2fwVesLnt+GsFrO6ugw4+E8KnnC94ltRT76ocN1nOY5w/is5bzQv4F+M0QY/ECeD0/9ih67wTSkn97agUERNxwE6b46wWXoRJxSB1JhnvSAIaVJv9h/cKe9D/+NSK8k6mjhoF0QxLDH9h6fS6gcDNFr55dV58kMOLdfothrRYMz0csZgsFRD+LQd9cvBUz1NORynwqtS8RWOOwwpwI3KYEO7YAX4JSgHVsapDyr87Oe0ojhSj1XfYpEzelFmcYNoeb2J18oOGWTtpHy9QiJrkyN+O9mNr88ihBkEUhoFfKLSwNHMBK/+d49VDDuCtnyKcVsMt2Grw/ZEqLYt2Xlweo/zzaHiVfjwPQh98lSLledVi99a39jKO0jOCdMYe3glvmnazaATAZzzhUhX0Ky7VDpn7V6NGtg9zSZiEUz7n0rYldNKiLA2Yk+1Coi/J0cyI1BpHRM9HyCAesO6XC/15xpyAhJT+AlXGZPcyWPpl5XuQWZet7liFIpmkvmT4KQ5QycA7ghZdUE74faShfVaUqeRicNklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwvmIZviEQ49o3CTCqj9KCyEpeYfFmGPWHuRj+en5mO2E7hvOnf2vGFI3F6IQg1oIEg3k8r8oS+kKTZXdt/6enCQ==" + }, + { + "type": "Buffer", + "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsf46DKX34ev/n8SRi3cxGBCmV2/IZhZ7XFSVuadJmoyH+dz/UEq8ZAOggG2ih6PggKl1waEF6bmeDVt9HmA99K3mpNftYD/U9HeO4ZluPimmFn/JS0m6rNMy9Da7eQf8QrGUET+zkkcDxl+kLX2s/IC8qgG/k3GriLSSw7Hy22wAqroYfAyiiB/QBHyKIoeceRxO8o6UU27c5iq9qB+Z3aFylmal4o7aMLsgA1D5tGmqTK7NBUzy9le+QozrTcrq2VyROuYMPxDKgO6hsR8QmYLSGsz1QaEZQ4IBis7Bbv4WG+LohLJgy00cTfmcaRMQw5+ygwn6dwyvY/8XhKAU0tvLganmVynHlqwUjgLd/wKLI1ksm1MQ2zdg4r1aNf9NBgAAAHGMXWlmiez+Bt1uJnIJLiYxY75a4tnUcqQ7anXQpUo15bN0ZEAMRZOSTTBZjIEEoPovmmDVoNT2i17iXRdml9qR9cdNtaghFFWApvDNVbVTHzjCiekU/wSVIvn5/1WpDJlT465OqqW2d7607ZaByNbsGKoIWL7H+GWT9C9bAk6jWzK9Y23HvEEpUlDR04tCg4qiNmyZ5NPr/ww58X+hR32NukYgjUdONxFIYFpqcJlnia8adu07ERfdoG17V9hqghj6zr5MlRUbD0skMFr5JDgy/Sv0GaXpSXC1619uBtmUqhyW7ulf7SKrN8VDAIn6UajiIF26Jl1uJxVLfVoVxwg5k8VnYVCaR8P2Cm11a2dhyYvSXLANoc3Crui4xCI+8HFV4eB6KXQp01hpJ6dNA628zct/6pFP5tS2EphjGZPcgW9WVM1xesV3lkFDebI055w5f/tQZjRhRtDkqFezDErDRBIXuUCDsivEah+l6ccwrgO51y46NxBgWYrC7A3GOP1p25WpZkMEEnV/GZ2LSeQGyPy/8FKzgHo+gLVDyvpKO7uePvbWs3vcc2xzlO7qzrzOWokNe8VSHYFvGiFLKkN0bVtg92lpeWuhWNjiPSMczkGKzwd3/6XdP0WPczanQlJbKNOeoEV5ZRP+SPXv+cx7jz0u73OoLroaGs1ndFPgQCfzRfUf62FUxFVd0U0aFMnSRQtu3K+W1QmIh08LfdPWABakAm2AiUA9uzYat6O7E6UqnzFX6uwGesCiUnTUCV1TdJrrS7fE57DlExcy80P3KXaU7k/tZSA6Elgx5hd+bXlcZRunI1Kn8xb0d+oPUTX7dOYj4xE6j9MishxxPBzIHD7C170tiBEvKDobGSZHOsPcAho8uvqUXcvAN22xJXc6pyfWH1+HMvoRJHtA6mEg9T2ORyBlILJEdoIBgyTPE3E/yqd27lYAY0PJHg8JqLPiL15ZihxhXfvBcn3Pa2TPXD09vEawKjxgVSb8TcNUjPgUcdIcxnCHnsLf2aVdwErvutecZ/gwXEJqI5ubkXCDDTRDRX+rK8+HQVptrtdffzrGCFftztxGrDK4IiWSaOVWjOC9hRJfDBZQ7TP/VDstweDNPMTzEkfgfNIyrmDRUxA93G8NGcxOf/8GdaVYRJ87aZYZKSsEG2YuxbtwJPz3fb8nkQ+BKf+x4Knfo2TjLP5Xz4EOyCoWEWiNyv0MaTYGBQByNQzdDkPRAPDFuSw4vrGHLRunbYkTF3o4b1NLIn5/uK7GUHE1o4ENf4/bgZAzCEzMwI1yMEDexp4s15BzPWGwuJG5o6c//vSC90DjKUKFE6caTEGn7IWLTFmpkV2RzSPJQuV46rMLTQmh9J0RKTqikl1zS+DAixGec371JUy0cxvEGPVYfIAgCZEWtIRkcyJZHMjph/3mtzuLfkatu1YyMYwiO9M7EOgEy/QPt1j6wL6VN84zEG61bOpNGoxe/kxnp3GCneESt6rnKZ5v+GGGLORd9pZAjr6XCXgL7y+OhyEq8ns4zUyPsSl0384S25HGgJ5zHfmBO3aNrgBvtMnIFNMFXE1ybHGup4SNsQQHe7YSdoBbbD6QwSHxDQ==" } ] } ], - "Wallet getTransactionType should return send type for outgoing transactions": [ + "Wallet scan should add transactions to accounts if the account spends, but does not receive notes": [ { "value": { "version": 4, - "id": "64e75cf9-efd5-44ee-90ae-35c791638917", + "id": "50376cb0-c18c-4a9f-b2d8-48130a3b36f8", "name": "a", - "spendingKey": "846f74914a7f7fe27a35df50c4459a65af4b8d1317638b5c7d08ecff5a894e07", - "viewKey": "df507062f8bb6759ff5626cec5a59ac49a173906e94bef23ca185bdfaf3a691e918383a46293981fb85b4971a798c56cad7315f35b2aa11bef4116f449aca84b", - "incomingViewKey": "15b6ef66795b5a640221a5fe863b69542ca92aaade03e33d4c828b1aee050205", - "outgoingViewKey": "ee464e9cc173682b92fe04a12db52e03e8951d02d58049776c774c450cebec77", - "publicAddress": "fc1b87236e98d329028c59dcb55139dece067d9a8092fc02f92bb83d5afd6cbf", + "spendingKey": "0fbe0f47e68c6ae12f774705faf73f426c3b82687d9f6c436835b405555479e9", + "viewKey": "6fccd442bad1509317a3483da3e1065b5c5f968904c6e64cbbb23b89f275a6a3a42004ed5cfde6716878f66d504ae3787322dd024906ca4942bcfa163fcbd5d3", + "incomingViewKey": "cc9f96ea76668ecca95562334d936d0deec92e4f851b201941480a73cf58c207", + "outgoingViewKey": "392d5baa236ace5670483c774b1d89f105ec83d2c878e08f4985300ed2a09145", + "publicAddress": "a0fe3b97b976c443140e07deeb5fe308bbaa2001b9ff49b4ad86df9c8d272f96", "createdAt": { "hash": { "type": "Buffer", @@ -7230,7 +6016,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "dd2c457f45e3131857b78dbf882f21d5f825f81362f7211860f565ce9ada9d05" + "proofAuthorizingKey": "769f765514f867ef878297cc07f437344893ac7f82a85762d39d95eeea470906" }, "head": { "hash": { @@ -7243,13 +6029,13 @@ { "value": { "version": 4, - "id": "a4e9c220-3d1c-4e4c-9d24-4424ee2d47bd", + "id": "d37ffaf0-847d-4284-942b-c83d69031d48", "name": "b", - "spendingKey": "9a915605737f1a94b9cc8db0d7fb3d7b4e6cd0c3ec25a2b92971d5d32677121a", - "viewKey": "944293a643ac12d0fd3f9528dab304777b10702243562f8532b890b4acd0a41fedb2e3045bf3385e577dc051807e559cb8ef243292101e473c07d0980edc6d72", - "incomingViewKey": "13de79e7e2ef97fea168a875f3f78d0b4e40bc17757c46d673475b992b6dce01", - "outgoingViewKey": "539ea34dcb6523714f018c29b17dc0450fe1735f0acc7980e74038a1ac7f88b7", - "publicAddress": "5ec695f11f12ae8e29f508edfef57acf4a2f1ec4a157c803c9d6365ce119143b", + "spendingKey": "6f3ea4580e54053f905b710dcca5b09ccbf0cda779fb4edffc18dcb9225be680", + "viewKey": "2f963c00eafac58da9fb02ee32d2d57112a57f6d84827a817844452300b2450ced5ae3a4753c0904327ce7e680150388f840daffa414be9530f45b3d57da51e5", + "incomingViewKey": "4bda4aecbc3ae34e47d40ae5849e5348dfc59f2364ab6b1130f85d7ea2500304", + "outgoingViewKey": "7c9eeab937c4849bf319e38559a73e654ce1ce067e8f68d2516483661747d615", + "publicAddress": "d7b74f04612a48ba0add85013e3df5fff7e4e9ad71d928da534eb9550d6d67dc", "createdAt": { "hash": { "type": "Buffer", @@ -7258,7 +6044,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "640914444bada025c574a7553cbad78e573d6ec2fae65c1df5321db909d1e609" + "proofAuthorizingKey": "e2419be2dfdbe1ecafb99d0e09d27ca845d0ca4745323e77b2a576d46d9ab207" }, "head": { "hash": { @@ -7274,15 +6060,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:kD/0V2KfYuAAuyhjJ1rEUdjuFosJ5ZOXBGS6fa+MCms=" + "data": "base64:ZVsuuTLt4XhvcZnBSSV6uaOWDkjBTAmYHoL+zv08BkQ=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:qn/FQnlKaVfstPrKsqMgT74EPCnldIDowffOxo8imuU=" + "data": "base64:/vA3ZYYNHb17hemzV6AksqInp+MGQwTtFaD41tZmpv8=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538943989, + "timestamp": 1718998717727, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -7290,95 +6076,117 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAARrRd4fMOdXv6ERlKatGHMjzlPZZxMB2JQS/Er5qjq8GorCef/Bk0Dc5HW7+t5L4U8e/YW7+hV43jtiD/wqQIa590jbWOqPPvYzrWGKZXBguXnxyq0EqhayGREmDIApDdFNxWm/Pn/RvXiIbQ71FLAo+y2Zphnk7RYanKTOM/ohcTx1bgVF9L54fk72WX+1LvY2JT1jr59Pd0PjyveSrpjICvv7bw4WVKbEgrrOCBrc+rt93jsA8lJ5a3L8R2ykHOfWw/hfGcn6wycv/oNT9oFggWBhntgE4CpLpREmVuP0DSlzUa8Vv54f/thmfBT+ZcX1BLbwZNvFdqdlm9meldKt4JIrXWxYwowFrwYdJV5icZza1/39PU8OXLvY48ItE9Q6xuYmIUGQiJ677uTcIELMgB0cYKPN3BsCz3k9/kwumdBHlQPXfnIFQkjCrXnKTxXiEcL52qAjgJaniiCBlC9idGL9bijOYSPweGUa++/R3mYZbtibA6IsZxqatBw4NAwruXDsS1iEoMrwq3lA0uIyovbo3x8LPVflanA2F8SQpjxnpl+PNm3z3qwAtmNH8EfY0S9EN81cvPI5tucJglsOcuf2W6OLxSu+8eKZW2uvCHb2pkSFIlMElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwQwjNX1alENo2d91FWMLxrni6nUGPSHwxCzMmSvVpHSVN2PVwQEkrHpL1m9y7WwWDaALzaxqHs7+TkIYf0IeHCg==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAjefRxfyJ8okVx86BcEIDpaa2kstW1lXDQm8IZH9IY3KTdjy0tgtVjdpcbOGB3csSu1v81gI0ykaJVqCF2Umltnx0n0MqrSAmJwCPKgd2AX+iIVWDK4hCma3etSmtNlMfnvGWGCA54YhqDI7omkG/pSWCKbARQhCSJv8SmQaQjhsA82bhUKiLsfHpQu6Ztqnfz7OCWvWwItFJHJa0Ll3De1J3+rut+nm666F66y7jRG20WCzFyVBBLAZgGc2JiNgOJcwo4Xa9yLwVIGt7OJzMNsGRE190MtcykPUMb5CJQJNTi117Q56luVOHZ9hwhoj7NLswBz5DKoEmJ/u9oBhbscky2pJNiJbbmuCcFRKXmoBfYU5cewVoys5hddOqcPAWBZkVre7mez9bwX/Y3HHtrlt6RFvfQAUuOBaNYAnA30nDDE01JuqqnRlo/uRpCa0XmCEfpfsmHUbyQy2IgZcdGUQAu1keFRbqJPEINmnza1MG3iVt35CDEWC5rp3os07qU4DussT/m2T81QnJQJS51adCg4sU6OXujBDSSn6VTFxzKzg0l0wZmfFqynAfgmkrFjDf6B+RgNPxR0plYkDU8Qyf7P7RHiq6OTClqgn48739Xmq7NSBeyUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcXPRTd7YL+fhiiZj1zkQA8oXhFBArzNDklDUIBmoUZYq8e7ot2HkenZ1gMeu1aLnZBLODeiQP5c2FZmLVDsxCg==" } ] }, { "header": { "sequence": 3, - "previousBlockHash": "40B02466CAF9ED8415965338E5CE0EAD1E7D27C275E6CAD55BD573A04B1B9034", + "previousBlockHash": "DF97E204760D5DEB4EB34DD842375E408DED59D23EB68EE93EB7A576FD62B48D", "noteCommitment": { "type": "Buffer", - "data": "base64:wxUR8U5uQ5+diof3bZLH1O8Q7n5E+T69JYjYZ7aaczA=" + "data": "base64:tN2/rx1uS9oklsWNiQHT4Pujqkn2H5rxhsVzcazDmGs=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:uiQHkSFVPaWVvtcrOnglAc8idhVSfYoagSvqphapeC8=" + "data": "base64:K+Rq7lGlObLNMuI2//UwZ/ehCYQyh+z1w/TfqEy8ZZA=" }, "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717538945379, + "timestamp": 1718998719572, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, + "noteSize": 6, "work": "0" }, "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAo8hSIw/8R+iPBJtDF4t23PlICjM8tzIy5+Mhmu1cAzWovsye+5OC8dBlnCWItgwZPRSsJBkOHjfVxc7DhXuGft+T1UsaLtOXizE/tjKkJNeIfYhREDfyl1hkVLuUgjXbVo0c2hvwfdoXxSBErwRt2bQ+7rXXSxlh7oTW/I1xJL8N15qz0PBLg+QTjsPp2L2dZALD80agZlXOjnflmxYX2bzgcqVd6M9lsRbUdnsuSnKUOT1kxw0I1cigm5IipCFlaUwNJteT6fan3M/MBD4YEViYg3C4bFdB6RjW37/AMxFNUcz4QKCPjJEhUH94EcWo+WegWsBNykALQNuOOoiVybTDdLhRK+VQ7/yoXv+qZIddNQYZlhSTNwP7toBxzy4rBBXwp43PqEh4jhQvgstcGWYo540HFVLFYiQ7rrje31abqqtU2RrGVtsQrxZazxqXwnrJsd8FTnOs6ImbGDP5PdNDE/OoAh5ozhWM5WzjOuummjzj93sx4G99naHuF8Ey+VvFPexFY3aYLzni2fW5/uXibLJI4y5wtR2lvtMj7mLfz2Tjq0CUAbArYtwbyJdVLPO3cOiJDgOcnFBc5rJ6poU4vEMPDFW4XMihGL+SnMPW7VEJ/JwZMElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYWRFBFNE4rl0YdYwffKKrmmNXZD9pTHOKfqya37ws1u6kD15uzqCrw+dxd++66U4o67ghgJujvNcYQHvUZkrBQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdiUEf////8AAAAAjn4SqHnpJxr3xEfdcSrwlvWIoEWaDasAuZ5vUfgiCoKuSkc3jW1T7dlGW41k0jiaBeOJRc+zUBj+gSGzzjRG2ENvggcoXgJyjEwgFt8Bh2SyxpnA2Z3vzHqOJiULG11euI7YPgcDOTTWgefBjhvh3020lK1NvnPW1NJ9fey5iUwT1NUaEnBYHDKPP3KUVBeSSLprn010P9vrFaZJMP28T3lsiDeAubaG0xOU/Os3pTazewIQOeq2iB7m/ne2WmhxxGdQBYgYBcIPmE+A/K6mTTRxLNJmpfBB9il71o3FvJJBOFC3RRlIXIZL81HGOAhUjNckpIePL129h2SBzMgPZWo9pfFcRTQIMgUq09L3mx2Ya9UdBUJDD4Itm22Szbket0TZyVoELvKdeYIvbMc1NAKwZ81K4dO9GdC94TtUOJK6zv7SABfxXdFqFTY7PcgiqX+BsFCMhCavExwQra8hFUkHxjL8K+AgYTXv6Ne2Uswimjd03oK2NEAwq4Z5Dsf5NRfbo03H6a/m9jGtaXsikR/42TT3naFRrrdLNsJxkk51jOGg3yi8sx9X3w39+HhgOH0I+EnSnJzmf/f+fhXv6BE6kDV+kdRWJByTLQDHN3BqSesV6JQSj0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw3YCfbUGhIenmguozd1lj8ctMn8wXOl+ZLuVbZlC8Awj8kucMYJ3n5OCT6Fct7V8+99kJEKif90l/ob8RcMEbBg==" + }, + { + "type": "Buffer", + "data": "base64:AQEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/5M1dwAAAAAAAAAAM2xsqx3frTSOWm+6ENKY9y756KtJ3CDwyvveW6xlwNOr/LdsdENo3unQFw7FLWUjvp6jekw3VXe0gRXbRManpQp7NcK3odB6NwKT+SXcM1ewVt9hw2a+meFS4sHMeFA09uM1CJMLCzL7jcciT6A9SeS9vcOa2UHFAN2L0jKtRaAXREMBVslN/KRIEP5Epqbdy26a2M0xLo18diUy0nb/6iwM5aClDKiqyQRc9Afvw3SGWAqibRVcKnK/95b8zL4GCNypMZGg0j0KZuZ+tSoadSvnXf2Fh6uNlXQM8X/xGyACMyLOAB151BuEhI7RaXYwnc4fITADa4+K5gPso7FuAWVbLrky7eF4b3GZwUklermjlg5IwUwJmB6C/s79PAZEBAAAAJ5TiFx0SaOjKaHCJUmLmAiKir+xFMtYsM/z/yTkJx4U72zhychk6tWtCaq2RWuU+wmwjtHhPAFbpuZCCbJIlZO/ZXmgvXLITzAI+16T4h5fedRbdjcsHC6sRpvidV8AB4MAu0k1AwXvXna6hHEWTnLjcoEQc5rk8iMhF9dTjuuXsan2PIDQeap5VZk5xmCYaoygBpklFBg5lXFWEbK6FW595U5sv1CPxhibEviQS4W/naqk4RKBKbERtVroScJmNRFy+pITLt56oXVO/jSd4IqB0vs50h5y7cMtGzG6h2Ia7Bnsq2pcYwpB/eWuIbB+gY2iB4Zs3eaRrbpgEGfpIcUKIAZzlCJV0qnmYXyJ1IdF7HE+Y2N7uiNDVoDke4SA8LTKknDmTb7sRopOEd5/+6rKwxYfMxd8HmdRAK232aPIg/qxOjxPCdQ43eRZ2CwQnuAKXY0sJRQ1L+nA+hjiLUIhf52DvxtFp5lDoCZwCUQn7dtDMXjm3FPqYaRFnNMHZQMA9WyApCGlbe8HDjmRJQhrzciXwUWPjgzUIu6VzAjumTAOWilggYPrScPtRGiwC4gn1feS/4u0Rf3S2EtEJzbtbsEi0zIXpiK3GfSwas4MzBVKqm9ZjAAgadV7kDKfVPUSXxFSA+xsWNHmXwKGig4+TFywvt9kpAmGAFluAhouGxeJea+Bi6JAgspVKi6OH9dfyLy+zHmw1J+xRs/cNEET8cht9gT2l0pPTaEpOqeiNI+dkQFzp8GwJq/Z+cOkTuN1oYJwXiGKLodSDaPoXXye3/0B6uSU6nugo+dunCgfTcc2UJrHGYPKRASwzIf9My17sjhnLMuZNy+wmNS6ruKgrLSI753bEL+EgVjeL6+uGlijAJm6uQSfdPgtF4y/lPGSWNDkNooF" } ] + } + ], + "Wallet scan should set null account.createdAt for the first on-chain transaction of an account": [ + { + "value": { + "version": 4, + "id": "98991cea-da6a-4bbf-8cc5-4fdd27f81271", + "name": "accountA", + "spendingKey": "6c5a641a371df373dd39dfc3ecc0d984999ad3a7d0a0786ab13d0e15e1ade682", + "viewKey": "09fc30937fd87b9910cbd75580559afb6825c020d589d66ab3daa550be6a0a9bb0c6ea88d93cecdfe7cc675c523c6a2bf7cf2f04f8f2e1f2953bd9be95a49539", + "incomingViewKey": "5db14e89c345663d5f066d7ce62e2d22f98a3c8f038c068bc8d48bab84b6f307", + "outgoingViewKey": "85de8a992117c4880fac395536df58f1eb2ff4c06634e30ee9393ac90ebd457f", + "publicAddress": "e9c1fb3d3841e52e96d6cff8eac4e8f5cc0f1bd0a07a5bfac5bd5ec99cb997c7", + "createdAt": null, + "scanningEnabled": true, + "proofAuthorizingKey": "d71357283f7d946802c5893f6bf994c6faf8097caf27adba4f04b87ea6724904" + }, + "head": null }, { "header": { - "sequence": 4, - "previousBlockHash": "9C5B62C46AE334039F95FA2D1283B13CCF490FDDE4AC28D78BFA62B7F90456A9", + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:DL2vFwoSCgdRX3juXF6dXT9YxBSigN2wXN9V7ownxVk=" + "data": "base64:TQXdLry7EgfukF8ipqsKdRTM+FJcOnnnVczBUoke7iU=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:1LvYS+S1Dn96TH3DwZo3mKvnZmFLPGErmtJL8Cp+dr8=" + "data": "base64:6oPCNz+EGoWrITI2NYnEYeGjbiOcwv/JAvMLq8dTqKU=" }, - "target": "9237817552710710459325694168790757901687427753068917234625768156097641", + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538960856, + "timestamp": 1718998720696, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 8, + "noteSize": 4, "work": "0" }, "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAA/LHvmXP0C7WunnQq4s5Sb0sSsAv7OM9rwNhnVxL3jqWopCzCiW8R7M/1UrcIYS1l20OqB/HXuMbW9FJiyM7290ZKA46AX3X6M7688rXQ55+ZiM2ODDygDexvMukbR3Ta0nLxUay/HDdkYkiDYZ0xKwhPKo1KZWjd/svnQ6w3gMEBBHZe/plDym+VJTAWJ84+/L3/cg9uClWIPob01zqm2GESETQzjeviPgtndP+9Gf+o2MAH+3ne0IyfPEqP5s1mVggJaNOwacMIEKMG4kY6GOkKAzQAT/ThnjG3zMMke46cxA91SjrRsyXCXuo2bjvJpoE45vypUdONEI6XRiNUJ30g70leY40A0KefYfWbBEV32qMtHFTxPRpxAV5BpI5lIBR1nOpgnKe2XZe/fsGquEQoi6vRDw1EIsqpV+RzqKUnz3PXr7SDBs+Np/ukf9bKV6iL8nn3/SuPAJqZMBX/6DigDo9h4elsBfnT50X+yDAti0FzJlGUXrnghwvtVomqgAFsUB1LRA7ooSOy1ZHUumA3ZtOkfYIUK3nNfEfo2dlrie4yxWFnjPdajSPGyE7pZiExygSIkRYkPFV/RbAT4wp5gUJAS7xwExEL6fjOkA7X8hIAh6xDiklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwFrKErzF+1B8ZY9ppvwcve/QJqV5qA1G0JvfTdbjTQdwBFbJ1b1G8nhrJ8Ypo+uOFL1fvIldpZwQnEDt1GNYyCw==" - }, - { - "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAb/X0X/HI7xUdgYl1jiGPiR4t7HdilFNJ1ImbNhdb+3GqmA0ZOezL4zI07AqSDpfDCVxGFUWVZtI71GNgXkYgffijxVP0HB5WCJY00I8pr/yILWkGTmsGx2ba+nCETyGQzTynYOHQDPSKCjP6WeVNQYBGH1NS31VmfQabfM/BBmESEMsMRGeMnMVkVEfpIJc+RC6pk2X9V7/8sR2He1f0eaVP3nrumv3RCZ06HlWv/qqk4jNc2bYxPuX1QbEcN1+cp2jpwjaZVj8Mz7bCq0vuFVmh4MPNgIFzBkXALZDfr+8r42ClnixuJnRnycBDTR+Rxk+30ixo2f8H75jO/yZKpsMVEfFObkOfnYqH922Sx9TvEO5+RPk+vSWI2Ge2mnMwBQAAAFxR7JF5Oo2tKN4jwUixToZndxikLOQEZNNxXB01L5mCAyNgCmShjCt/7QWttLAwWnyb+r04vzOlok1r2bQFKN6t87sPBvP28FFHv7i101RFKYReRYTof/CrTgpQPKqMCIbjqqtSMFYv/Ecp2P5bmN2Xir/5Y4rrJose9rF0VqDgvoa9zqn3Ei9lkAcyHTTUa6ak0XMP1ilQ6wdnR7XMU8M24DeuVY/hqzlNIiBoVciLwQXPNZy2hm+rRneLDNRYvhnmaBEuzloZ5uM0yQqK+Luo0N28+Q8bzHEpz3j8VAiFbDRgeZw/LL1L+mdgW0OxK45s8LmwjtsOAYLmwD8MIlPU3jR75tgMMs+UBhyft2OBW2hEYkt0+wm3TgS9z248nl10eHEuSCtU3TKhbLl5oyPOIZcHDgUTf/GtX/Df4Xo19Xs54U6ordx9+ScZk8wnCibCyUCbSb1YC2nYu3yDpXMqEGoGtcFJOefQl0gBMAm68YjAsB6yy/ibxv62sqYIJs/2LHc17sb2WKxTTkMZxppQrtCadW4FqnS+hujPOc+i9gABoKUyqZCs1IMumY7g3T9qPbq3tnY8QfgB382mP5PRJoLmrX4lPWXKUVTqrY1RsteaqnYCKF11mHY1rBRxpcdZJgqntDNreaapPwDts/U5DDir1JXObJXJOVDDOaUajllDNz8BXkikmV27kYa6i6vEywcwTRIXnx1b/Hk7xr2tV27dHIJV5zsZ3zrVlHfQEii7xxQIm1sN6+O97hiyqQtMIZWVeuLtlO2t7b24p0x5ADrKJ3JSvzcE3qNszUTf3k9+6fIAcKyoSp+Epd3KpmLa2t00adRUyYWMew0z3w2SFot8+TkgZiCBxjderq9bcwqiSnIGVeSL6zVh1AVgUX3zwdw1CbuRjTYwc9Q65Ftz9ew13CEUWqS+7PTW5tl26Gje0oN8BGgMrFstW2tF3p2yHOeik9YX/UY+4WI1tuk9E5fdHezUQbgZKnqt0Qw1lQiCxbMmLS+GBZwxgR1fLKo0B2o0zbWA9+kVBkBLHQMykP6hhqWMgFf+s05kAYslZlBaKNcYO5OG0zDnpunoTSTcCdtLVizIW3E074MnKvPuKMauuW9SBS39FTJ850PyFCeRz9ExEeModGNZFtFjyQDu+DhhfO8UNAKyyn00CfhH4YVprDWn3fCfz6bj1KOqjSnuWXS285pppECdE+nVE2V7LVDTexWYJyyjmVlRY0m0NLTNrjLRWxzT0loESC45zs/M66e8EdWM50zduL1aAeQreNpaWwBGg8rUwQuuWomwyAcVo2+GgTqRC3kFCtQSVaHB801WHcguech1wTaEN1hl9NjMCEOWYyqaJEwUoNlz+lhrlrTfb0QWCXz3d+X3Ci7R2xhyNv6Dv7+HIHKLASNOMkksbpyTUdkYaXeK2B+vs3shVhFX6WnM8+0jZq0ZT91XBybLPUfZR1LpwlYHaBLvJwvZGZ1TGlItQkJ4aHmcgtOQmp7G9p/vV0RQlTIlO96ioiSHoui8Qv3Qp7uSltg9Ip5GCu9WvlYzhdTeShCnlrWhrtrYO0eJCtGN1SG8o6v0UBK78mkVRlyedx9LBw==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAm3cSdXbX7cKHnSCrfA+gGBUvoBPRkrzbSOKHj6Jj/musrgqgsGRLp6MtrA4KrDN1sc8KjM8UpwGNwKUqz9+gXA8EGlp+V4I7tELelqBbg/uQkK/LUqq0uzEoRdOswE5dyA44piCvU1cbbteME37SzwX5e0bBmDyavD22IuivygACQtkSLdjtH/rjU+U5+8ID3xPRAx94raBT9GZMzws5Rmgkh/5cOiKi5UnlY3B2CS6lckx8r02PYALueYyHBq9BHqFdjIAx+ZSX214tP+T5SMnlBhG6NSOEojSu4JQ6O7HUGUnIf2xhwLsg0ftP1CdHKB9GfFHnDnfNOEDOGKA7AfJ8qQWlzTjymKxWVcmsd4UjUC1MSKlo3igCU2/x3ZZedQfUp/yiH0A8tW5lhwvZckrGBQ4mWtLAk21QYd7A8AQprr4wwaQ10mRe7s00L3e2sH3g07d59mAalpVYqLm0sKrKzF6KqW0Z9OVtApvMjxIat6TW3dVNHucaLvDQCFRE7c1M/F7n/Jf7fv0/2Q7k7tD15AGuRCoL5bWfS3fSJKg6YFvnJOLHZngbGyTq8LOoFuUYDRu8aeRliMaPZdv7Ln3CMwqa2JPSpdC5wiOJ1SJZNoiQw+fs00lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwJ84t4RTp8dTjF6gNHcHGuMgql9+jfMDOV1AEHjHQkqpp+qAzf3chVdnOqj2Hh9CwA8kwKnpcIrsh1grpcnk4BA==" } ] } ], - "Wallet getTransactionType should return send type for mint transactions": [ + "Wallet scan should not set account.createdAt if the account has no transaction on the block": [ { "value": { "version": 4, - "id": "1752aafb-063a-407e-9a89-fa6cf21251df", - "name": "a", - "spendingKey": "3ddf6ef059038d3e00eb9c054356b73243d47b655afb53de6780406ed467b70b", - "viewKey": "93cb0e9ef0adc763018cc7cccdb287024460bd9de22778d649bc021f436275a5bb98700cc9e01b463d2eb1c6fc73468874076e37224ae57fff95d51dee4566c9", - "incomingViewKey": "e1d116d102f3832dee495bf0b8e22e9ac241e394c839f4a3cc6c17b70a1ec701", - "outgoingViewKey": "c481bc21735be6f39e11cdcafba614db85f4183153b8b539641ea0830fd3c11d", - "publicAddress": "f0fbe34510ec326ba09245a4d54837dad8b32031a2d9fcad7cc79e66f3effd17", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, + "id": "a31335b0-59a0-4544-8168-d812ed76e603", + "name": "accountA", + "spendingKey": "87b2bd40fd9bb27d8cb5553cd63f162ede5d5b95b988a09f93bb0373afca66e7", + "viewKey": "112d5ed92dd70bae4cf2f11d74d93ab643ee0953294cb9974c135faeaccdcd4009444fd2ab596ec499a47d599770a9e2cc4460afdaaafa354bb1ca449bdfb206", + "incomingViewKey": "2b96bca15ecb9cb436ca4d770d3d8a4b9545cd353c666c4dcc4fe77601a50806", + "outgoingViewKey": "357c78b412cc8fd39cd0ca4d6387cc5efcd7e3101465607734923b39d463da3a", + "publicAddress": "e7ef66ff93467d483d1f8b42e4d569501cfc567448ec7b302bac008da172f4be", + "createdAt": null, "scanningEnabled": true, - "proofAuthorizingKey": "ae8e40d882b798ec0ad76c8cd52bf7930efe746e038b86607520c2016388910c" + "proofAuthorizingKey": "6c50131848a61ce5f7d26de20ddd6286864f022ab88c78704f9b858ee540210e" }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } + "head": null + }, + { + "value": { + "version": 4, + "id": "a009f9c2-b16b-4500-b246-2e94506ebcb9", + "name": "accountB", + "spendingKey": "21aaf945865c7910e0cbbf02aa1aa9990907451c0bdd35d71a74d2e007cdec03", + "viewKey": "0f7035d736159ca841c02b6df564491448a45fd054a3d4cb2bd33f5a236f57942e93ab131eb4a97e06e2e3671f99d9052f7f63f2309e8435e0137a9bcc027616", + "incomingViewKey": "bab6b41768380ff997078c8305b688fe92358b181d7244c1929bbb7c1f8b8d02", + "outgoingViewKey": "d300ab9a2c9e0237d9555fda97832feff2bdbcb73e58fa87ab67031609694ec9", + "publicAddress": "368de5243f8a31b2979a7b9ab6234e6d0d4e391e393a894a3e4febb77e323687", + "createdAt": null, + "scanningEnabled": true, + "proofAuthorizingKey": "d37cdf6fef4c6dadf450d22d8695ac754930a7b110e7fe48d2bc0d7daaf53e00" + }, + "head": null }, { "header": { @@ -7386,15 +6194,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:onCdNmggF/2oGVp4f/p8oknj0nj8lc/SLDHFGX/YWUI=" + "data": "base64:rno/E0mfHjie+7Kc3aS30qPrnU4yZFtw1DlqCfdLExQ=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:52ERR+LGjawjWBROzSmoSEo9uPzsNdDEI8sI9O63sJo=" + "data": "base64:nHkAiwD89LJtKTABXYEM8jZDg4LqcgvyM3nVaorULCk=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538965971, + "timestamp": 1718998721727, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -7402,56 +6210,92 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAArn5L3SkJFV6D2iI0M6BIYRDQBFYZmqWKrv4hi1j/W6SXq9VIai5CjFHYLIaXlJAEhNtquTP6IgL9896gqhnPqbdhbd6HycGg6y+OqPoNQwOE3VG/Mv4hPqMWnmzBsGZKnWyg2+IeIOvoKSewBuEMklUN5o0uFz0XRXFI0wxXuVkMSJDOn/Nv5yedmJBfu/IqEux6Brr+a4GnLZR6RprhtIxRnS0srk51wjCjdqN8peeQ+WiWTwgJa/MzU4l3v0OCN0rWHZl4b2Tt1dmb0RNpzW9UB+YFe1ATOl1OzosOKebiivMk0TUKtpOntjzGXqLA7gH4pdN1FepmKPily3yl5IegfpR7B+tQnjCzPXWOqSs5Wcjm5HcwceztZYly9yomBmTrGZ/peNaBim5F0ckO17BTQjQhS/8AZIo4SYmP8VuLirAmnRo1oy8hgGynYBzbTbJDPj3HEsT1xQRzB5+ed+28CWVuwfpA9tC9tpFpSWFe7HX4cvIQZBkojzPG/1Py+dODzRnQLv7VrshbnpcwAO2UPr2AHLG8Fwc7zIAZ24F6eNHN65aHb+dUhvHXjTP4iDlHg6yNXllku3FKEvc8kVQaB2LhPHW3lwKnNBYFQOlWMcIrwYuODUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcjJfwd0I9efczfg20oav1ibON8LckBbQkoXToi18vhnsAyplf0g+wdX/SK2N5eEXIyK7pJ3qeVlt7H+vHCijBg==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAc2P9rmGqlrHZ/ADOkq/2N8hhuWHb9hcqIngJz6OSyeW0sYPMCbG3ngxXoj1hvRpoNQWfoTP3MUFEAwdV520XHN/nQsGZqkjzEzC9opAjd0SmIsjqAsoeL1b/6XgpzHp/M0RhEEAq/B1XUcG1jqMwsKjHpjeX831joS7aW2kyBY8SuHCVlbbBh7fp9lWIa/fi3IYN6DSsyGEJYlPdvP8Hq9Pthe+OnTjRfmiFoqa3DwSUCuVzGT96b8LXdANDvI3NqjpY4WU0kmfPjKm47nXPL5x/NwD2MJVpC3BbOWuAz5l7baRoAzMtnkXII1lZ4s3mipJVG4ztLCAnJDSAhrg4Kh2uKZUL8IcX+1E1oJEuUK95C++C3idzEJlNnc1YGdFZKBC95GPPz0c9VmViQu2a95iSh6Kox0KvdJnJUtHwR/G16Mqp0no7V3DHdqBQQQFAfrWKlFS/wlUY6+6C7o3p5s+P2lsoEwnsAdXMPOSAEbrcz7T1I5ldjZI4akSmoK1nSWylneCVDOTwQgfyN8d5WEDEUyrOBhY9JOIMpGzc6PQKZ5YJ0VZbai5RmTif8Bxxl7R9hZdlXV/PxrQX7ABixov3NL0E/f2GG1v3Nyk3Dla6a/tZPoZBCklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw+cugJ+ZCekWQXEr/BamcuY0rk+UejCo407FMBBxjKQ6/HMcCUJWq2u0ZwRDSVrYhjWCmDox/ZUzkKdJop0O8Bw==" } ] - }, + } + ], + "Wallet scan should not set account.createdAt if it is not null": [ { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+MdUUPjPr8bYZCJtC3J2W4iA938sRS3Qoe2BZJzBrgWKvokrOsO0O/hDpcz5SvYv1bSNzwldDXrJoRIzVMB+cYyX6fkN0Cx/oAZlukvFI/muwiPA75GDk8tHuYvEoDvTw7WRve/ltH+QC/NJpquGnsv3svnkZj7qar4pEEQbNpAZovB/TUJD7Unzoae4escwbKsFkdh67iuegyxt26gIe8IHn2kcvn3fS/bsQO55NcSXC7CauLu2F3gmjLbgq9KaOpbbwifsHGXM+zW/UWzL/V8HL9FAISt+4NxP9wxwvxa//SRdJ7MguUEr8JXVSQxK5Y1xL+gipkqwzwqOshkcNIGpoX4013FSoIAbcnzrBqHWfhGihtHLeDyC9co8Lgpo/kVYSvJnqgtopdigb+8VY254wEvt8QA3vByw115LBm1x5yRh3mAxFa1aSjedz0X58ErncHLAoXqOXZbsnxLACEtjkb6dulKxdvzl0MGQBZvwYDCnlrq7t5M1NJlnMNWbCm+zmPCTEq4lLPRKMj5yoO0p0i1vDZqSpoatJbFHE6xMFcuyGC0DGpNHRCoFlfh2TNXLkaZl2OKsob4EjsS+vqnT5yCnIGbe8PfBVWwN5zwePJrYYJspTfBUnQ08ui+eGoprZlSX6jYdY7h/lO7TXalWQ0iqeZGcNlwLwF/wlnSTnyuGOOuzAxuSfR+TbvyIunSmOlLl9+2rnJ6f7vRly0RpUJWc0UbMp5ReHEgT6FrQRkJy2nAEWEKipTKuJGYKzt8RxpaobwfYK2A+9kJYds/lbtE9twVmj64i/CQxWL+zkurINVrYaSMbMEckGH47cH8xEzMFdc0cb++2gIvRLrJd7nRuHbO+ELzsz48eW2lGZL5PTuLqZ5u0sxzI7HOs0Tscv/xS4t8BAd8djZffsitKJDqPKSTpmRwkpkCBtxymMrFqTXujAkrxQ4vWUK1jlES6PNB5dntroZqwWXBNALcXRL7lNOrP8PvjRRDsMmugkkWk1Ug32tizIDGi2fytfMeeZvPv/RdmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAKVllL5fZK+LL8CsKp+KUlDcVZSF7SgJeIKXj6e+BAY+rWp+ecYfUBZLX7ZrP+JQXsA1JfRbjB9cPp1n6ltc+Ag+smvti84jb2uZThx+QUg3rENLxJc8EeuLnnMV6fA3pt4YSEZ1gBEbth0piU1z9vPdnpyi8jS5HrWwtBHLKngF" + "value": { + "version": 4, + "id": "b51a2177-7bf9-49d5-b3c8-7020567c2f78", + "name": "accountA", + "spendingKey": "641c1bdfcfb9c53b5fa91b612d083e66f03cb3d18e7a733b9e6b12062baa8b4f", + "viewKey": "d3b69546923d9ae28bc804f2236e10e8a9209ea8327792bb5810f18079185642765e95bf26c434cda64e5a3e899c6f9cbe8270dd163b8cbbe87b7b3e6e8b2d11", + "incomingViewKey": "e10aae9a56527576cc9fc13320972cb64795c4e09b2a49033c80bbafd275fa00", + "outgoingViewKey": "24e88559218318ed0d30fb12626f7ebd5d1eae39c7c648f3344925c2e4f8513a", + "publicAddress": "0951bd228d8b1d4d0b374478d0358ff586c4372f1bedd7eaaac4c5672c2f2819", + "createdAt": null, + "scanningEnabled": true, + "proofAuthorizingKey": "6bcffffdd490ffba451aded0449ea15d42f27df3b8e7be94e658d309e8563807" + }, + "head": null }, { "header": { - "sequence": 3, - "previousBlockHash": "87E305098235CD4ED1E59AAB0E750C25DE65C2808B5142C0DBF463975747C6DF", + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:xOp7eiljMucd2lTHf590CbEP6FhPcEsBkx8RuinEsxI=" + "data": "base64:CeM7ZiqpJ94aF3xuvweO8TbeF37XmbC7kmG8B8pDATc=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:Nixjzd7o7fnd1LWmI1xLyZegfs7QSYMTwiLyggwrzgk=" + "data": "base64:hsNOgWGdqZ5Kqzhnm/t36IqzIC7khF2HYAXQL2+olNY=" }, - "target": "9260366780148527510972123832573278885902566341756516011968728852484845", + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538973214, + "timestamp": 1718998722800, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 6, + "noteSize": 4, "work": "0" }, "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAKF84UDt0oViCmGUPnejc+ywBy30UoeOGW2bUrCMI34Cg9rR2FynPxlwGN+QgmBxjnEHIDdLHLIvswKI7uV9lA3kCQNrhje05X1q3cG1/zHWNXqOUYWwN0igYCCUH3eWBNR9pv2jTtH5JTGomyYvQEbqQkTezxQfYbxTCUXFZ39QWxxhwHrM7pg4krwcwucTl6DBpTnekawRDmg41eUZ4qIAI0vwjkzvIWiebFupRaICATpH1koxEMAwzbUKeNntYeWCNFqmeX656N73yeAMfq5uOYkv9Drf1PWRTG2t+bud0UbN2p4H7MYha9Ih4bPgAncyt+JpIuo/aiOsjzwE7kRw5eJE4Yz6IKVRG8UjyoMrpiqJPAOcMeWn6h7WjaPhRz6i6+dnYjaojCXuQj5xmzg2ppthvdABgqVYLJ2eeMsICvfLZ85b1hyYjcaLTbKiBzq3ccxEHQrBMNm2Tm2feN/898gKKGCqDD4NaRzVozuEm4U3Muyl5yJG3ErHYdPXYCS550Joxk3PQl6S7w94yAZKMMUXTBatCVcc5sK/7/LU1gmjBmkjp+8ZKdTnQR5In8HRh8HygMxNhPA0tKSO2kAYGG8EdvMsdzeZKndldn7pVbhLGwuPQ/klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwqcoKO324naEHo7H3CsJYViRrjbMdiIaOSYTVU7gVuHC6DBbVm5CuAu1beM7a+33QhqICVQS6w4uRSaj+mJrNCQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAn5mKakjLbopC8b5ZMYWuYrmA8HMrzaVfHwpxYS7BNdaAP3OobriYPDls/HjhK+2EimlsSX68i+cRJPzZkYnBQRlqt1VkURq5lcA++FaCYfGpN6tbBWzY6JyxFJvivYuE/Q/boYalvm4J3J/aYNN4MNp2q7Fp6wyxiuipBInyXXsCpSbWXbkJxOieX//N3ZIpbh2BmNkJzNWC3dYbbLxfiRrBeIBsxbe1KgwswZ+qfwKyh5LdabL4R+42odr25+AtYb0Yzwirq8dKt+/uiKc7MYrKUmgidhypu8kNK48owiK1osx7aOJsxKWnLIl86rz2uBni01HShu1utmLrYJDhhY6L8X0bsEH5hvUhwklnDEeEeQV/g27GnFrdE9CZ0QtonFxbIysdXgyb8RA6cBhpfdyAAwb9Eref/FvUx4wxm4P24VEBtEuHwbaXAam1/s5IbzpAKChX9t8tlc2Hs1pongPsa76/t1X0N7BxBlhYfSeGVIw9mEUFvTpryibIf1IysgU2r53gnr9pj9TO/Yy3usn1SF72mda/zCRVcTO1YhwfGkTyMEm3WNByI7zqlLeshvDHHyQHYVUhfLssbRdDIZ1OHd3etfOqtLJoMFvI79PmNOwQlV3JzElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwnYMsLZBmmyxiPmVnZO5txPHfqPHzz03HI6s3tO+QftPoBniGe05y117jKTLztGOKR4Whtx+BqmSSv0oKQDkBAg==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "F68930008A6CEE0775619E6B57CE0E00E23B8B3D46DDA0AEABFE278208A5C932", + "noteCommitment": { + "type": "Buffer", + "data": "base64:J2DN23oTrpIte4AeFhXEQbTMzX1UTOeZQI7CAnw/4Eg=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:wLuMntvBDYmme1IFV1r3M1J3eO0bTWoMaClwFcxAclE=" }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1718998723311, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+MdUUPjPr8bYZCJtC3J2W4iA938sRS3Qoe2BZJzBrgWKvokrOsO0O/hDpcz5SvYv1bSNzwldDXrJoRIzVMB+cYyX6fkN0Cx/oAZlukvFI/muwiPA75GDk8tHuYvEoDvTw7WRve/ltH+QC/NJpquGnsv3svnkZj7qar4pEEQbNpAZovB/TUJD7Unzoae4escwbKsFkdh67iuegyxt26gIe8IHn2kcvn3fS/bsQO55NcSXC7CauLu2F3gmjLbgq9KaOpbbwifsHGXM+zW/UWzL/V8HL9FAISt+4NxP9wxwvxa//SRdJ7MguUEr8JXVSQxK5Y1xL+gipkqwzwqOshkcNIGpoX4013FSoIAbcnzrBqHWfhGihtHLeDyC9co8Lgpo/kVYSvJnqgtopdigb+8VY254wEvt8QA3vByw115LBm1x5yRh3mAxFa1aSjedz0X58ErncHLAoXqOXZbsnxLACEtjkb6dulKxdvzl0MGQBZvwYDCnlrq7t5M1NJlnMNWbCm+zmPCTEq4lLPRKMj5yoO0p0i1vDZqSpoatJbFHE6xMFcuyGC0DGpNHRCoFlfh2TNXLkaZl2OKsob4EjsS+vqnT5yCnIGbe8PfBVWwN5zwePJrYYJspTfBUnQ08ui+eGoprZlSX6jYdY7h/lO7TXalWQ0iqeZGcNlwLwF/wlnSTnyuGOOuzAxuSfR+TbvyIunSmOlLl9+2rnJ6f7vRly0RpUJWc0UbMp5ReHEgT6FrQRkJy2nAEWEKipTKuJGYKzt8RxpaobwfYK2A+9kJYds/lbtE9twVmj64i/CQxWL+zkurINVrYaSMbMEckGH47cH8xEzMFdc0cb++2gIvRLrJd7nRuHbO+ELzsz48eW2lGZL5PTuLqZ5u0sxzI7HOs0Tscv/xS4t8BAd8djZffsitKJDqPKSTpmRwkpkCBtxymMrFqTXujAkrxQ4vWUK1jlES6PNB5dntroZqwWXBNALcXRL7lNOrP8PvjRRDsMmugkkWk1Ug32tizIDGi2fytfMeeZvPv/RdmYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAKVllL5fZK+LL8CsKp+KUlDcVZSF7SgJeIKXj6e+BAY+rWp+ecYfUBZLX7ZrP+JQXsA1JfRbjB9cPp1n6ltc+Ag+smvti84jb2uZThx+QUg3rENLxJc8EeuLnnMV6fA3pt4YSEZ1gBEbth0piU1z9vPdnpyi8jS5HrWwtBHLKngF" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAOT4hp3kzfrJQfa/A0mGj7voP84AvUQajpSsBdj97I3CvILgRKetnGcnBe8ffBA2aUf8gdbAFW4TdU+NDHL28tA0AacTubqNK5sdrTxiHfxGl5C9JqPAs3ATFmeElsHaOWSaqHS7mV0cCBuH1M0JEuzGsaKNTL2jRvC9rTWApsmAV1yKqiAspfwMN166BvOxrskd7uPDN8lWPnwjdQI29aZD4ZU/ihjM1W4A/ppIOhKaEY0mIXpeiOzgWA3EAIE1APAPsJR+7z+CHAvdHWFe+IMBzHsOsWz+vuMbtDU98c2WsDjRG9R8G2kqVanpwyloy7SZg686AQ5p2wDbMeMDfA+fww+zD+p87VoNL3zxP4Nzw+QnDst1tM3zpaQouHa4T0rt5OB3AFBrZMAcOEZ9RbT/Ucq+hGa12HR2WWrugM+KpgEfJkcYXZ18iLAWKiJACUH7DUPmrszPtpcESfarxluXhjOQHHOVWLCI4xIEZqRwsfAdOvoMIXRi40K0xB+UJpHANOE9eo8Ss54gfXmIbhLDpRkfCeTw/ms++0vyPt0j/DM7wEF2XaFccF4l7FuK/cgR/RYWC6HwIHmKlQ/FiP7dV7/eY4ChEzvpbNPVnED6+Wuk7VE9Dhklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwV+gJr6dA7B2IZy8lSR8WsYLaRBzYXKisGusQfhGTjINnFFWXdvx3VgP0qYv3c09MlLd2r+fcRBlPyDC0Z5DQBw==" } ] } ], - "Wallet getTransactionType should return send type for burn transactions": [ + "Wallet scan should skip updating accounts with scanningEnabled set to false": [ { "value": { "version": 4, - "id": "e70d60b0-ed2c-4ecd-8733-6868e06a1f07", + "id": "1e4922c6-aad6-4092-91a5-2f338fcd26d4", "name": "a", - "spendingKey": "1e0f64aa9f3399ff327fcee2f958d2878b22c330976591ce22260ed49b7a6ae2", - "viewKey": "f24aa2d07299bd40c42bde10abbac5d2eb8a610ed05e69cb09e058427deb3aad28024d98a0418fe395b0923a0063926ec3674da1b2d70a08d0d6c85a332c6265", - "incomingViewKey": "f9203e5893973e234d850b65573003ba04f89c231195d0598a4198c446d89102", - "outgoingViewKey": "641d47cd60fb9991e6c62561f53f3b557f04a285ae6924cc5914718e9fbbe138", - "publicAddress": "cec7be662b6bac78af667994b490f26857d18dca1e728488ffe7de0b01c4404e", + "spendingKey": "50defddc864b45ae3d92b0663d1ed98ade1d7f4cfc7da5609b73122abae7862c", + "viewKey": "bb5ca714f08db91372f4ac368253bb91675cab092b2a443227f85d30498aacbf12d3c65d86b6c8342760ba3d9f5789959ab875c70b3d77b93ffb582dbe6ee8a0", + "incomingViewKey": "d4f4638e9621a02550df9c0e7aa46bbd556a6cbd5469a860801cfbb85682ef05", + "outgoingViewKey": "654d1cecc91700d92d4f1e0d6381ad6f27e593fe587f509b45dd337cbde0aa1d", + "publicAddress": "4106a42311ec85d37716faec49be530a90885dbb5ed126bb0f67d69ac3839ef3", "createdAt": { "hash": { "type": "Buffer", @@ -7460,7 +6304,35 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "23b064a3b8d0933b948996bf738832e9f7c65e859c7112dd9b3ab9309db3d507" + "proofAuthorizingKey": "fb887f4f928541aa4ff3d1a93d527dcfa29778255ad7dd6cfbe24e1725ab6804" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "value": { + "version": 4, + "id": "878e5aae-8a5b-4a0b-8b81-cfe176fcaabd", + "name": "b", + "spendingKey": "17bc6df6fe6cd3500b85aebbb86a9626c62c57098b63d2ce18ff125b9bd89185", + "viewKey": "ffc8e1787c9bbbcb75f3ba139943747f3cc3914f3c4e87e6531bbf3cdeb7da6f9f750bc2a14677d316aeda30f0c6919ffdb45c3166255a05652ebcc7c6893cde", + "incomingViewKey": "bd582662218864859210d8a719129a19dcf7a879e4d05a13597abf7023199f00", + "outgoingViewKey": "d7be5cd02680db56a1eae3d0dbbc4d8e88743d21ef2581dd40c07be295f5c584", + "publicAddress": "5af1ab5b21b972f6cd638eeb7165b67d1b3db81164f4d0f17ca5ed0e9713d1d2", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "ac3815e4928d9f9eee9faf47f3cf37b389c6d439699e5486df2c14449560630e" }, "head": { "hash": { @@ -7476,15 +6348,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:aS7ASLGMZiv7hpmqlHeNB5FRMyV91rVJBc1tPoKm1C8=" + "data": "base64:R+jWuC0JYf0T7xNmD6XGJgIcuA5oePRNISk3RICRaQs=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:AuB8iwuUK4wG0Mq67TGU6TTpY4xgsEAbVKgn0qS0tI4=" + "data": "base64:JsO3m4JXINrXPB6EI20vO0aFSyjip9mhInzjDrLW/Kc=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538977678, + "timestamp": 1718998724419, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -7492,90 +6364,48 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAT4+USY6FnToSV7ZjcHzWvUT9p6fvxmTd0dFshKKHhaOwV6UxnRaFMxwRGAiRQmkQs/crpIVhpvceXbUV0C/YbCWZr2K+T0GRnnxGzHWROC+y3YMorg10M5SUK57UFTD9z0pZhMJzJTqx4Wt/JLwXgwbEzamfBEBSQNyYM4ZX+NoZh/7ySogA208M4saEhys9gwb6DRcPdIpXozNsnnGN7miACrdJcyi/oF9akZyooCaRsGJCV4GDZ00lnor4AESThyoGb2xHW4YjCNfqEbdANtuECOKFGMc5B2SHb4h3m1gmH0T2wBHj9ToomU27LHKtGLe5XdEU1ibsfrwgFin0P7R7AJRrXhcw2xmxgiy3p1DpsCJyzEMSM2ApROZmxyYEkSAEnZ4GUvasMfxQh06GBZRVgrwDcRzod8HAcaJaFNExqNMayN+hVYOz64arKT1yAMGyKsdrhUtFz5HFVv0EVxz/tjwZDulRKp3TPwKCVw/9Z8U8etc7CNnBNUM/AAfuLbeXCKyrAga7Wgc613aUtt2cm91yRg4zREMCETkzgYPVqS0K2iXmTqha/GRiOC2pCYlfetpSemH4eOE7ql1eA7XCulNxv2/rFeTznc1aElOagai+E7Eyzklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwmv67lpCUh67l1lWOSSZZrAwIbOxlwXQx2mUV/wjgZkqaS6b1DxMVg4lRdMz1336XflHtqPG5UhzuZdIy3XQRDg==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAABcSOR4fMrH91YBEfpzjRHFiGoXDVJNK8ysZAKcvxf0anR3ylnovmFXuUbPhhtuiLLbCbaa3ptq1msDsQ5wR9y8HtL9ucYNgfA9sEIp8PGIyolMqmhlhnH+vSVbe/n6ui25mTa4oMKmwlBtvY9zkpoKrTA704CiXKQ87wSqYtp/oE+Q0yDWSDxNg8FW1a4JfO7aOVoOQNEZlDvku8JUCc7tfeAB2kyO4KIZrw6uFyN46F2jaoVvrlx7+MYUtthgTutjsnVw1nIOo0ugOuIYGgBAEoN1+AEaaVSiupeMc4m+vEX+4WvW4F4jvsSxTgSMbWX0ooN05QjbqSJwAYhO0ZYixW8AJuEiJM0UEKXFGTmOfemL5IMCNP3aCNUCQuER0KHqi7Zo+R/hX1HK5yKya5mT9kDuCIHwtmlKsNCZXdIsdBgVyS41L7wRNx94YHNeVefzbxfue69hcPuR9uAU2fAaJDSifKrAvoTAF2kql6fuvNL7DzuLXYdUfy/MDYXbhvsJE+Gt9m5j2fl7pVxhQaygdOUHykt96gJp8KEIO7nr/tOHsH4iV1qzBiEHYSu0Z5JN1cYTGCvDJLQXmPBXq9AQqg3jF1dQ5c7Y13HZW4+Sx5ikMUQKMlq0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIlCkMe8MTJOaB+rlORtldvZZB3Hm0AaQCl/r9Ms/p1vvlNtgSQSE9hk5vCcFRBuvI0hHL0DgMjzyB4ATD+QRBA==" } ] }, - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE77QpulZtkpkkf24CYcBP2qUgX+qSRIB+djV1VVfhzejHnuJ4vnj4QJahrQ35gb1z+6a/BaGG3zTTlp7ZrOk1ieWdwIDTSZoBDMLtRkTi2q0QwhnZmIUJ+i9mqVLUh2MYvsqnmNs9ClsqeB/fYPnwP9ZP3w/KMCrx3juijlfWjQNcsLqn+SMA3Q5ApFY+k0ZIX4hyLHWbOI4VCOb3Jei8rlQgo77HmjLpFOnmcLLSYyp+RsVm8BzJ4/3I2GADRJL63S7AWdej9mHqCRgJSnLxqEZLdTwyqHpyqsmfxKBHOKBnOW9Qn4pUWDHWtSGi6fPEyvdwSBWTMxalBkwujTRaB7hArdVpXzGZwbLKvCaRqPSjpSTK8W8ggd7u6WVbsZb9JWOz3+ShA1lZh4xsPsv2P1uqulzIzNoQglCPj2wnDBzMG4FFgvaUr8pP/XPpeSKnSbS8PmvQpAcifZZK5l4YZaBOiucPgmywFCpIwqgjB4i8a8UYGjW+tA1LGVmVGnhy7e/muL/xlK6SEKDtgF068Fpey7+rCsXI1m4wIxVw4tT/Y7aWa8C+jhqxHGOObv1hp13/71EQE/oE2WOE4cqcVlzTP7vUl5aW2IMT1k4yRVqNy6mNG7YgTHpGB2YC1znaYP92M0BJkhnyJG85qrgClhZgC5GzlT8P0umRD9IbgIyIajGL1kxZrHHtIImqywLFuc9CgVTlmBkJbO9RtJtifYuqITaEJ4bj6e0M7/fX+N786SrzdkjWWLWqsGfRaoszUx5mSYl1h0UJFY4B8sNq6U84A5qBtgIjnZtF6sKLFPky9Vtc7iY0ZXLxdo8NQhf3kyUlKBuZoq4Mqg/0Xuj21EFw4Lug2X6EXBiIeJplV/HCe9fxfq59ypcyA0H60+pVtUYuQ/amJ1XdcpYC1a5AAUbdGPpY4d2s3i2nTQEOVEON/RapXwSY8exLECBRuHHelTtBwDlm7sjb8kIfHQHnWLqtqcnrwNOzse+ZitrrHivZnmUtJDyaFfRjcoecoSI/+feCwHEQE5mYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsKAAAAAAAAAILb/O4cVis/DloIJXLK6zg3y2uKNGph8FYoizUSjKzfb6nKQ7gufjpDqROnU3F6q/rbkGbK7VKWMESV44HVDAdEabfmg+yZgHyrPqcXoXgr8Zw2LAjTWNg3brj9Kuwk7eq1xsSQL2MI0eS4HTCls3v4D8X2HV2Flngb0/vlWWIC" - }, { "header": { "sequence": 3, - "previousBlockHash": "216409CB9937C70C611CC4FA9BFA37518E3AA47236B7EBC514C71E78A39926F2", + "previousBlockHash": "A3202B2AA6172D4DC91676FA64361471F0358A92D5EF98B74E15DBBD43B36208", "noteCommitment": { "type": "Buffer", - "data": "base64:mK95bK5rOxJgUHgQ/d1RvV/JLGkdNqXvwt+wt41hjGM=" + "data": "base64:ocIRbw4Uovz4bkldejTirUObQWEIwYsZhJ+8OXKfvSE=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:2CY+t+au4Gl01WHD7KhEkKoAYC+O+FVMLwXrwcu7xLk=" + "data": "base64:OkCsn3smkIs07W+07rjSCr7ocka+/jAEEJ0b0wRWPCI=" }, "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717538981685, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 6, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAwIl34DAsfiNW+UPDnL2HSTpcTliMyVpHxLy8DYdIkdumwfJ25zQsU3N/1UaavA7hK7IhK8axVCZN/3kgNixHiHw9E1r1qiyRaegyOPP+RJO1R2IcLhaNqzQwsNsKoa4qq6j9qDzscfyuNO6D1EJ/ILY2kNEFjqWKwd+B2I0i3fkRBJVCweBA/ctV3KJh8E6qgysP5lNwaSvBO3jc8RE4/+JviszvLXTyQArXDUjzVu2I3wkguvUbnkbO9HY+MdKM0fRwTRUxjSbYSaoDWrJ88SychritYLhoLlXf1a19kt87HsCyUmYvHXIt2/76PY3O48X3JBmxlsXOUVmVGerwDkcEsYeFKwejca+GPz/6TtcbDy0kGwNdnNU/3EHotKpAL1UGFmBV8Zab71cXH/Pxtnwsx6swtrvuhsbK+B5JZCm7yrEeXnrgL1uX09nqYi2ktmWGJmAhE7pio837v0DKCW2u1V2pisUTAiHOxfngkQ3PFr+YCJs9Ah1jq8pO5nl2vHUQwzTXl2wQQ51z//nYaipev4kXNrbF62tNDi8eg8FVbgpEAk8Uu3wMSWLSA5/PyN/T7kkTPJSxDaFhKBZx0f/apTC++RK7W5ngLfNNcTGZ03XSHfyX9klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwA96BvNjRm4Z61DvxXmKG+5jFYELBdvqecqwWOOZpCWBWIaHcE85CVEEgJSstW7xB2DRMqh78BRlmzLxpRxYwAw==" - }, - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE77QpulZtkpkkf24CYcBP2qUgX+qSRIB+djV1VVfhzejHnuJ4vnj4QJahrQ35gb1z+6a/BaGG3zTTlp7ZrOk1ieWdwIDTSZoBDMLtRkTi2q0QwhnZmIUJ+i9mqVLUh2MYvsqnmNs9ClsqeB/fYPnwP9ZP3w/KMCrx3juijlfWjQNcsLqn+SMA3Q5ApFY+k0ZIX4hyLHWbOI4VCOb3Jei8rlQgo77HmjLpFOnmcLLSYyp+RsVm8BzJ4/3I2GADRJL63S7AWdej9mHqCRgJSnLxqEZLdTwyqHpyqsmfxKBHOKBnOW9Qn4pUWDHWtSGi6fPEyvdwSBWTMxalBkwujTRaB7hArdVpXzGZwbLKvCaRqPSjpSTK8W8ggd7u6WVbsZb9JWOz3+ShA1lZh4xsPsv2P1uqulzIzNoQglCPj2wnDBzMG4FFgvaUr8pP/XPpeSKnSbS8PmvQpAcifZZK5l4YZaBOiucPgmywFCpIwqgjB4i8a8UYGjW+tA1LGVmVGnhy7e/muL/xlK6SEKDtgF068Fpey7+rCsXI1m4wIxVw4tT/Y7aWa8C+jhqxHGOObv1hp13/71EQE/oE2WOE4cqcVlzTP7vUl5aW2IMT1k4yRVqNy6mNG7YgTHpGB2YC1znaYP92M0BJkhnyJG85qrgClhZgC5GzlT8P0umRD9IbgIyIajGL1kxZrHHtIImqywLFuc9CgVTlmBkJbO9RtJtifYuqITaEJ4bj6e0M7/fX+N786SrzdkjWWLWqsGfRaoszUx5mSYl1h0UJFY4B8sNq6U84A5qBtgIjnZtF6sKLFPky9Vtc7iY0ZXLxdo8NQhf3kyUlKBuZoq4Mqg/0Xuj21EFw4Lug2X6EXBiIeJplV/HCe9fxfq59ypcyA0H60+pVtUYuQ/amJ1XdcpYC1a5AAUbdGPpY4d2s3i2nTQEOVEON/RapXwSY8exLECBRuHHelTtBwDlm7sjb8kIfHQHnWLqtqcnrwNOzse+ZitrrHivZnmUtJDyaFfRjcoecoSI/+feCwHEQE5mYWtlYXNzZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsKAAAAAAAAAILb/O4cVis/DloIJXLK6zg3y2uKNGph8FYoizUSjKzfb6nKQ7gufjpDqROnU3F6q/rbkGbK7VKWMESV44HVDAdEabfmg+yZgHyrPqcXoXgr8Zw2LAjTWNg3brj9Kuwk7eq1xsSQL2MI0eS4HTCls3v4D8X2HV2Flngb0/vlWWIC" - } - ] - }, - { - "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAE1J8HBgNQSQ/ZwFj7dtD2OFVEooj4VItPMQhtqRNNbKzUsO9C1ImuiFKLhUEzpWRbPF5IhgeD8VcvJnWWLyC/mmSNZM9dS1/9NmGo9Qb4KK5kqHj/Mb0DAe6GiM7ksVwIpnZn0aEDD07vohnfgpHRcSG5/xeD5G9fNHlO9346HsN64VZGXEJHvxEWdkpjPeBFvXGXtM9pbqB2diUQ1i1qP9W060lZ8joZGIA/PTU2y+GlCXP5WEh7fcoA6CYuebrQYF4/aEHt0spOiRa514jFHI54jbcpQxEbNFAECAPJLITLfpcmgk4Nnj1DqOEF8qQPQdYkkpzPsSv6vu/IhAlQpiveWyuazsSYFB4EP3dUb1fySxpHTal78LfsLeNYYxjBgAAAFAcQYJ3Bjq80IkYQGlRobMuZrHVChVRfKaZI8t4870WcA+2DPrv8jMttuiYGurEAP+Y5oTlQBoHuMzQTmmLOMzKjmps0sXGaTm1qyIss3xMqc2CC60VRcdEbk1vDsVvAaWwS+yns3B+uLn3GDgZ722Ak7R2pIXks4Lai2UCgmaaF74bkbbULeNB8ly6jDZl1ZToANqnpE9ExyZPlW1oJVaPNW/hZ+tZKI2TOKMWgcp+pLeIsscVA65ZFpl440aotBb30DXZr4u1RXEi1+o9X9GCb00T18SahJNy1rg6IUcc2W9Ng+cSUB5c8C8qAS1qKYdHUk7PMj7bQx1a7zqOYXuIvIvALh6LePls7Y1a/09v6iEEbCU0UJY5U7VquVMj+IhcfpvXN5iYcuqC50QPI/GwI5qN0R7cysC67bQEfKqmzf/YMYc8WUf8VBqX59oU9zduR9aevFzwdMNNNRFr+hQ8pbyJoBupGRQ1MYoLKHgsVyKJabsgvZvqDqk/3Wjuaz/IIuBWNVUMmXV/Cap+nM7vSXVynOxFKsZiupZJA/cYjW9LSx6tZP3mLoKCn9sJOakXu68vbOifl2BA22ksZO0vJXl9ZgEG9/uE+iI7mZn0aXNWiIkrwyUmWpFxY25Qeeo4tEkH6CJgDqMQ8QMfAcgW6n+//Ifzzo/TUezwoAjoUZYY7daOszTIhY6zKvDZ0O4weI3KryPDBbLwxd1Yzd04VnF/PPFTlMxg6hepb+CRfvQP5HnlKGKUQ8Uq5wLB5zvRp6vOmu2W2Jq/twaQlF8ZVOuzyfHC3oQpb5/Q9Jp8tBtlSPxp2Zy0STzNBJRcKFx87GU9Q2vbV/qrj77lqIoBxrjeDLGrSwIAAAAAAAAADQNXKeAIKXatYnxF4RvRddJ5EsDu4ittGEX3M350r81G1g686V86OMk1c5iiGqaVDRZ61tuvRWO750wKKFJxCQ==" - }, - { - "header": { - "sequence": 4, - "previousBlockHash": "28BA7F2C93974C90AA11A503B27C976B6EED085470CDF11A6E8242C77889C413", - "noteCommitment": { - "type": "Buffer", - "data": "base64:i1hZd72Q/bi6aCdWOqGg4I1ixsl+M48AxPfbG/X5aGA=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:FotkS0I+a4FhPq4s9AAPgzPktQ/bWyZezOo4EA+Nmsk=" - }, - "target": "9233318228143625020618577701423519925017621426082203201059080050516648", - "randomness": "0", - "timestamp": 1717538987224, + "timestamp": 1718998724870, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 8, + "noteSize": 5, "work": "0" }, "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAALVdeHAARF6+/FPvLKt6au1F+pPgisG9zBFVL4JYvR1m45/gQDxhSDVUzWnoEG6JvR4btm2AmemzicsIL48qB00kn1K9y5Djtm07eB1DBrCOS+1dICZZGpd75VzCRjLczVUWR2mAjLoHo7iO4YKiov2+nw/k9RSjomzHISATKZUsSl+fwW/+6GnEiPXAzAgbsKItpXNUVwXW7qag3afvSr0M1nCHHBToAOqUdpxSDjkKwOLVoSVcgYigagqIcYUhtOHthUFEIlTYIf9GrPexHctQW64+ktX0/LhzotMbipFGSHXin8Mb01H1GFFn2z2d28qlUCrHpdhdGLJJyhdeg3Y2ZMYqsA9YlMtsM9sBlutEo9wRNtOuZuBgvQSgGy8RYBlox+/u82x9J76TfbOiYu5zaMdbhHPKyCV04KEusfMhVOWuZveLhhLTdwKBdibzSEhGz7NII7mSomBPuE/oF5UnMbaaT7ANHAgQPOKPszprz1JtT0YquQ+O+9KGzSQy0UAapPRt+fzQLsvBldcarcoBzW+ugFoCChcDA2KmLsOhdg9yhN2kFGOEAKmeBN9H1oc/X/YxFlRPIW9C+sb/adN6TOgU7ZY+PW+KYA521TaK1fCkf/ZmIWklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwVnmp2/WjL5jliFYsJ6hCSIiLkZ7VqivBFmkJO7lkHQa6Io3ccBDSdQidWSK+Y569Kf9LBH5MrTsMDAcA5UkkAA==" - }, - { - "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAE1J8HBgNQSQ/ZwFj7dtD2OFVEooj4VItPMQhtqRNNbKzUsO9C1ImuiFKLhUEzpWRbPF5IhgeD8VcvJnWWLyC/mmSNZM9dS1/9NmGo9Qb4KK5kqHj/Mb0DAe6GiM7ksVwIpnZn0aEDD07vohnfgpHRcSG5/xeD5G9fNHlO9346HsN64VZGXEJHvxEWdkpjPeBFvXGXtM9pbqB2diUQ1i1qP9W060lZ8joZGIA/PTU2y+GlCXP5WEh7fcoA6CYuebrQYF4/aEHt0spOiRa514jFHI54jbcpQxEbNFAECAPJLITLfpcmgk4Nnj1DqOEF8qQPQdYkkpzPsSv6vu/IhAlQpiveWyuazsSYFB4EP3dUb1fySxpHTal78LfsLeNYYxjBgAAAFAcQYJ3Bjq80IkYQGlRobMuZrHVChVRfKaZI8t4870WcA+2DPrv8jMttuiYGurEAP+Y5oTlQBoHuMzQTmmLOMzKjmps0sXGaTm1qyIss3xMqc2CC60VRcdEbk1vDsVvAaWwS+yns3B+uLn3GDgZ722Ak7R2pIXks4Lai2UCgmaaF74bkbbULeNB8ly6jDZl1ZToANqnpE9ExyZPlW1oJVaPNW/hZ+tZKI2TOKMWgcp+pLeIsscVA65ZFpl440aotBb30DXZr4u1RXEi1+o9X9GCb00T18SahJNy1rg6IUcc2W9Ng+cSUB5c8C8qAS1qKYdHUk7PMj7bQx1a7zqOYXuIvIvALh6LePls7Y1a/09v6iEEbCU0UJY5U7VquVMj+IhcfpvXN5iYcuqC50QPI/GwI5qN0R7cysC67bQEfKqmzf/YMYc8WUf8VBqX59oU9zduR9aevFzwdMNNNRFr+hQ8pbyJoBupGRQ1MYoLKHgsVyKJabsgvZvqDqk/3Wjuaz/IIuBWNVUMmXV/Cap+nM7vSXVynOxFKsZiupZJA/cYjW9LSx6tZP3mLoKCn9sJOakXu68vbOifl2BA22ksZO0vJXl9ZgEG9/uE+iI7mZn0aXNWiIkrwyUmWpFxY25Qeeo4tEkH6CJgDqMQ8QMfAcgW6n+//Ifzzo/TUezwoAjoUZYY7daOszTIhY6zKvDZ0O4weI3KryPDBbLwxd1Yzd04VnF/PPFTlMxg6hepb+CRfvQP5HnlKGKUQ8Uq5wLB5zvRp6vOmu2W2Jq/twaQlF8ZVOuzyfHC3oQpb5/Q9Jp8tBtlSPxp2Zy0STzNBJRcKFx87GU9Q2vbV/qrj77lqIoBxrjeDLGrSwIAAAAAAAAADQNXKeAIKXatYnxF4RvRddJ5EsDu4ittGEX3M350r81G1g686V86OMk1c5iiGqaVDRZ61tuvRWO750wKKFJxCQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA8Sewl9kPhrjCT6DVMfsB/rMJBKnw4Zoa9ZJvwoZzedWFfWg7lB3XtZhQgTy0GnaZQ6D7YgrdGmqQRwc6vcsTJEUL40Kxb/ZovEVB0tHyxaixZngyKSqCvmvrqWfJJtWgBneH1hBSgcIb3GO9iHCLpLLvKNFmeyZusuK9RnCuX54U3Zugm1nE7PDt6UF67gR4hHaiN8WRp/hnKSyQfRoOQUS91+6D5aCYCqacSWIgPgWkP9s6oAUvB5RbpYF7OK/uGwb7wZdLCVl/9zgh8O/VycsPYs0vVJPkC7Sue0JVIjrEh3w/MIMnxVGoBaVtj5Iow5/1YUvAl7a/JxSYuq1vLOoJJ0uhF6OJXoFJjZDI2RbXPLRc3zAqEXdZUUu1EvEID22SkKpwJmgW/TaYzy9G7OecPHJh2w5k1lhkEO7/CM3km6/MbrpFWCViX0RDRBVoYIJ9kX4tUOA5ZXUGSxkpVjnDZp6hhnHHFeeUMpFGRHiI5zGk0xPx6BC5sp4Co81KVhYQwcnPq/kfUHL9r36GHMIBc4k+oMU7WnPIdVDGlhQXxPCG6MO7ff6euBjJBgAv8WeSsE+Jf4KgOdxlRChD1awCZqe9G9yTl4rxXNkvUfez09hW+dde7Ulyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw3rmmGw6WUA70k+ltmyiVsBsG504wgWV9kCr9GDQrAi972Ha1IpaiTyR8A0yj5ylqLBnf3+p8kBoyGhWbAaH4BQ==" } ] } ], - "Wallet getTransactionType should return receive type for incoming transactions": [ + "Wallet scan should update transactions in the walletDb with blockHash and sequence null": [ { "value": { "version": 4, - "id": "c4cb3024-e8b7-434d-8829-9335377d66eb", + "id": "aa597976-6ade-4d4e-87b5-8113e3f2da93", "name": "a", - "spendingKey": "14693cb6c6e5ceedd843f678db9bc375ce44c7073f37c9eed6d3dea779ab8bf9", - "viewKey": "418a47bf9602431caa7790bcc6d72980af04ec20426d099a7e0f212bccbe9eacfe08b43fc99d13b7f87c7fcc34893d010ecf193e57a767716b2186f5d00cd5d5", - "incomingViewKey": "6d2e22e99680c3c63fe31d6398793f9b87454432b01ddb8482692eb597d66606", - "outgoingViewKey": "6c88876709c07716f48b19d8056980876a7cd803854f30c6c70091577a6ef8c1", - "publicAddress": "6ca52578ba0d94daee94af9fe5603043acd6efd1b1a9e8c54647f59b1cf18f99", + "spendingKey": "32f81470c56f97f4c52cd402f93f3d92839ae6433f682320ea895a7249d19226", + "viewKey": "2982ac68b5ede7dbb8eed2644df70e082adff97e6b68ffd4636d7704826a06038f7061c45cd2df510bcecec3b381f45a4588fa2841dc39d215304b1505f1980d", + "incomingViewKey": "d9b0d9e337cd2063abf908919b180e6ff2f9a86a0856ce449cf05ad0b3de3903", + "outgoingViewKey": "e7c1b9a80c8928a1c520625ceddaef379f9bed16bb994ea0d9a2ca8b42724af2", + "publicAddress": "52dc14a8935ec7e6bac79e73da8f145ebc1ddaf36cf02a61ff9ee408ff59c134", "createdAt": { "hash": { "type": "Buffer", @@ -7584,7 +6414,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "a8b6d0e280b9332339356cd93dc93653375ce5aa88d013394e07a8dddea9d409" + "proofAuthorizingKey": "b7f018120431d77811103d474ad0ce7885a0f93bf7e5b542f9738d9f0a5a870a" }, "head": { "hash": { @@ -7595,15 +6425,15 @@ } }, { - "value": { - "version": 4, - "id": "20efe043-89bc-48e0-8ee0-07b0fac2cb40", - "name": "b", - "spendingKey": "202d96f2615513c2ede0c1bff0cb0e5cc2625d1579f9de24c324097913f161c0", - "viewKey": "643cc5658542c1ecce532fc5a94e099521978a3cc47b5ce1552ec0dbf8128ce3aa620a8fecad587f018946a95bfe2678912b7e5acd932cf9988d1689722e7a0e", - "incomingViewKey": "9dff42b9726e1ac5354bae32ce2462d85dd985488ca3fad3579675b65cf4ce04", - "outgoingViewKey": "1801e8be9cce2b842805bb8d05744ed4b9073d1f2047c2e718c09715892d336c", - "publicAddress": "3bd5dff2753a8a2b0761c27f468cea32effcca7fa479d66abd38a70a32062a64", + "value": { + "version": 4, + "id": "aa99f6b1-bc95-4570-aad5-9d7350d021b6", + "name": "b", + "spendingKey": "02bc7fab396c8ffeffa7049c6954748ce9efe8e3d80f403407461ab86f115859", + "viewKey": "9f394e59eb9d01bded00ecd56092494d52ef401b33b4f0a6264d48ade1ccf5ced01bded720763c6ca99f93f6318d3c55d1463f206de35e3d5b4e431b91dc370e", + "incomingViewKey": "013d3e467fd07dbc3d362994855e1e3f760b33f5ee1f3fa4c9ca2eeb6595b102", + "outgoingViewKey": "2c2261868d1c92906c7fc63067916e0e9de04d842d99596743b41085d77af779", + "publicAddress": "52cc478f846e52c5736fd12fb3dc1bf5d34185f77cf20539492bd36cdf6054f3", "createdAt": { "hash": { "type": "Buffer", @@ -7612,7 +6442,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "f942a40b3b8b18debe70aae80f451b1fd280934cac610a51a82fe75264946503" + "proofAuthorizingKey": "ab27a089182c52ec712e77d2ee9b3a980c895cfd75e357f3f7947a07186c8809" }, "head": { "hash": { @@ -7628,15 +6458,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:UuhrkF4SAaKe7XkXXUMxX/eU7R+6uIiJXh0LwCbpEVc=" + "data": "base64:QTtGxhAGyL1j0nNoBVSltclqiCQrsibApaMtyQbf/Wc=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:KCOjP37PoO4h14Z+sNC07HD8JS12DtSF95A3Yxq3E1w=" + "data": "base64:RWDv0e1VGnDos0ZWR4MZYI7AE2mK55ibduV+nIyLX0A=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717538989717, + "timestamp": 1718998725908, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -7644,25 +6474,25 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA1K21zyIuRHaG9i/n5UhbPUTahRf/4H4W+mzCiPTAqEiwbAkw8MrpIm3E8pHrRGmU2HNXJ+H+oQ49lV9NIKD2Qe6okGoHDqIaRFMrSB++FL+DfoPlUyq7G3O8cmRHYRkQt4Fnnwl6v/QDhmDAtR4FnHQYlJKnw3rwSzneEGwlUpQCT6Hbc/KJd/jp5aEhQfmkXCB+YoVB5P9vQ/8/PeXxaa9xSLFZM5riRUjEjVb6WUGxP5wJ52DZcKSd2EFmwcf3bjSmUFYqloenbkqzGN2orjOoEQzYlcMECr9K3fXXJp2o+kvWp/BR209jz872Zirvk6dBazZvMJ40LZL9IGpGEa0eDU/0aJOW2oOZD+P9Tw4W9vmhgslgWhrbkF1/d7AZj+Xh4USFX55U6njxIS0ctalLqlASWo3bRmut2K7aaj6EDE/PUozwwftYUqSJNJv4zGEfGBzkAbvmlyzCqgrt5xXzS2t5wkEfklaNOIeX2t2Q1f/qQagdlCBSQIdRVEsLmrK2OKFub2zcQBI/RPpMDZDz4bXvBfvaBtJ46bd0hkJwn79oB0+IxCw9K/tNc8ZKYrKCLsBbikw5ejvKhTanPzXGv75dd/jQoYC9XZaC1Gy2PLZ2VBtkmUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBJSgfDD70PDdz4bvhExyqIAPyqo1n2ywSkzhWkWPV4L+DyiX06NGiIennDE2lDt3PLJ6vpbLNvCTuJp3+YEICg==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA3J1ZzKogOU3C47WORiXCSkQ8m70xzpUIc/9wXSvGEIOYDlRDN61GD699/0ZGCjtSwNajcStquhjLNR5yMfExUCPpxD3qEiFJ4CTefEHwrDSK8Gg+zOjuui38mVx+4JdUAGiwNRp4IBsaRGy+gbP2CGXU8TWQYlwFtZDraWAxp0YPNeFh7woEklOSLWHdQD165Iz2xyVPa3NrWCDGtBDYO6yUZIOy/d1NPXvvY7+WrEKFtBIU1PK0CoupmXCBoW0Ou/c7HEydHhe1kjqm1TxTr9ZHJ/S086t65w+jZnHzilpytmW4lW0175fdEG5ONqPP23vK/zAgu1hbmixz0XPPOCfLQVXq2HggXSzCA96b9129Wcg4upyNsA/b+RzEbm5FWXbzU4uxfy6WXt/yJE6So+lJ5c3gKTXuJdT4S3/wikLSUB4bWENKz4nwHmr2rCjIFRerSXGGg0JjCqn6nCJRR3mIY9tU1jgrhuo3fVUVf41BmEDuPzvy4lx41NU7jtw3LI0eS9Vq72/hnrKPWF2JVhUodkbWdgoDm9xvjm8mg3nSfBaMUC6IbiSj84KSB6nztz65IQJN4RcvV7vWold5PUrxO6ZE8wBkX21oUhHP4T8rUGF0M69EM0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwtXnZ/VzfzcL3ri5t0USzXR2/DwdG5f4YzhJftD99QF9+Yzck2MV5XpK1jBbxiPeFTY8ldLO45l7gOCKXSbnIAQ==" } ] }, { "header": { "sequence": 3, - "previousBlockHash": "8F3C9A535641712B724F5EF936F8136F5BCDA81DB0CF3BAA5D619CC6290B7A9F", + "previousBlockHash": "D766E02BFF74FA6273774954B60CABF52D665E050FB58631E30E7F4EA4BBC6A4", "noteCommitment": { "type": "Buffer", - "data": "base64:5XpURaP5TgcLXs7ttAKzKPPxuVeVdZsobHnDo2vLgzU=" + "data": "base64:O9eQaFpLD66t7aBv0GozR8/lPcH9VRfM97FZqsBBRgY=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:Adzrr8VU5KnMuAKnawAvcj2gZh/FhUp0FyXyK8lqUvE=" + "data": "base64:HHwO7qVISPZmBlU29haBBjYP3krd2JywcL8jfI+2miQ=" }, "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717538990741, + "timestamp": 1718998726349, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 5, "work": "0" @@ -7670,25 +6500,25 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAd60WzsCia5aaqjCnF6aJ1UydAjAz2I/pTIsZ3EVSOo2jU/SWIkYWD/J+4XSx/ExymiJXkc6Ej5Z7oT1vDcAHH66dbS3dX5QC+HxEwObJrGCEA/XjqgQ1Vdc/UnsZuEEw/ccoJWqL41gPubhQDDIXvyWICZCErjg9C40Mlpx/Q18Ksk1BMzVYJuVOSsWyVxwYgfR3EYCqFgrnwQ1Y1dqNKWphFJvpuvDfPH64lBpv9b2THjfG5A6t4FpGfiASD8tqcKJk9huNpi4QxkuEwnySxhl3xcNUablkBI7JCKTJNXVLS6PSwV6pOgZLg9QlnEW1qYYtpzTbBIImBjgBOipU1XmfzHF9UVbYz1IHyH8sihWIqHI7ncZlaSbl1amQ2x1GSqTrxZAZ+y0fSIYI9kZ6eSfnXE9yNexHeBOjCsPpQTs87GtS6byjl7AhclfkKHHx241Q/OFONj5I4E5qTlGH7F//93RI8G0nR341Fl2T7MnPaViGk+VW1068Zad3JYOyRvz0zSJa0RAu2yOmStFgVwP8SZOu8SDXtoqDrnVhWu9CbBl1j2U3i56Nm/YzzFivOT8gqRl/ZHItJDG4RbnyXQv+9JgwnRkhnEYq0569/hLbSYfCvuSMm0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwfP+B7tcXM8iblSEyZ7NIjkA+i6DPBlebCklGY03N80IoLIVFmvSTs00ITfc4HYEjGcK8Rg+spqUoBs//SqfMBA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAfGjm0Z1/yMIHLjaWKRGexawHI9fz5MYrXMp4iZ8iD/Oo+a4liZGIqPe0slCHfEF3DNcq1/7xVj+BZG4fVph9NagvNKeG1/Egr7IiuRRk3xO0bRznmOvcBxk7lGWTURU3FiaSC+VU/2f9Hbm4Jv+kPpx7yC9v+539wgp25fNIH7gWKgCfE9e1YsvYFICT1G/qjebaCWg3sJ2J01xM0dRaWaxV7XAtpBJHRhk0ba3mtXurNaDstCPeTZSuR/71x7kIKp4TS43A7gRNPxjNlCQngvF4bYbNyTae2+xOWfxG7ozlqbPmL5Q1LelRdrvG3meU1PKM5UWG8r8TElLrvvRMGyjRLDnPYhwWy1A4rNoNQuHiKBhMt5sWZNHrh03zntJA1Kb7g3HE2aHStoLFqhWTk/5IsDGJuDLL7/eSWWyNuwy0HvgQk9gUngVRdlxnSrtT32gcBeDxZdlDlJreZAp9wz0x9SHnASc5gBQko7zLA7tUsHvOHg73UQc7iFlLidaB5ph240US7hFgv4XC5RaVWlrtEJJbaCkDGGbD90QnRxzHtSZQhmzdubmCEFZQpcASYroU+rNbURkllhI07QZfM0Jt3leO67XfU5F0O/rT6BppbfPVeMJdJElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZGpVs31XVgcXJyT4T+MGSb3Gs+mmU5bqriJE5kEZa+buYF2Tl9xKApsFOP2+PSAZXbL8dkUq3TgvAlqsuz2cDA==" } ] }, { "header": { "sequence": 4, - "previousBlockHash": "4030109C2193ABAD2C1196BACC0FAD459A5554B2466AB2EF45D0575AE893A08C", + "previousBlockHash": "9F0C15CE5430B84B7FDE1B61E0400D4B4292E64060312DF36EFF68BD8FBC9803", "noteCommitment": { "type": "Buffer", - "data": "base64:j5RhH+NP7THvS7URjv95YuJAxQmARx7NlsFgETIiA0E=" + "data": "base64:KbDDm1t0aOo0yYessRIxW3MTgQ7sarNwShrNHJ33CnA=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:3ufKD/Tu2NIj6DUMd2pAp0MHYtxGs/Wcttu0MSsshXQ=" + "data": "base64:PO9wVkBUUO63nIu6lRt1Kz51iUnisv+M+fsqFIJMXh4=" }, "target": "9228823284279306817296266184515742822248210830185427859262273659833347", "randomness": "0", - "timestamp": 1717538995575, + "timestamp": 1718998728632, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 8, "work": "0" @@ -7696,26 +6526,26 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAAHJw1xcOYxGD9SzcMislG4w/p9rVrkr+aQg+SYuyZPRm1AGU5Y6frFpwXvNVKJxPRtGPMGBkHbJJfe4Fph8DLjAfxQ+4T6Smu2BwucDTn3/CAyy6aJt8zwRuWt7kjvL1qXco3PgVr1tW38pVDdDTMX4V1gNIhCZM/8xSjPly/UhURjqWieS2DuC/glh66pElUgoVH4GwqN++85C4h7Qtdepct+FgHYT9pAsUreVnvavCF2ba65DVhvhei2cnVYr1Smx3iUwemI3T/pjU1513Xh7lZPiwKgw+vpbMdwe5P1MOEyQDJrG3cUVgoJTP3d4bQ6V9q2zcbOmmM6Cmva/G7aBB2RlgEwT/4Dx5UKbnkEdZPILTpnXdEAjXm4r/HvSc9VqsYHulj0g7/KTR76fyx71aB4DmRX935gHtnmdx2NoVEE5vxwGRKmfwnIUfFmOroRXLFeAIgtMwIAuV4TPWiIVkM+xAm5q7nrhq2fJriMQjyDDFuyALoEvnqpyLyKY9VvTlkmEVS0pB3OSn/KK4HApF6uTyev6cPtwOho6zd/nl0kllfeAm2Qos4Tq5sp9tI1FBqDT8KJtThHQ06gHL8UTo0bnjqcmWL9nGmb/aI/gaJysn0Cox1dElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwhsNBOFeqO40AS+WoO72jhts1jCfAtN5qQVQ1Hf+MfbJW0wQ5B5Zz/TKrbnx3c2P5n8l9cXWaVATIjL/xePWEDQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAAtiOSV3p9zp6O+X+5e9vCduAlLw9LX2wTc/YfxtY2yqqguRCDzRStLxl6xKrK1X76/yFLtvk2+ACEJCcTY26TU9gw9WMeLwHX9d5+BNRQSxyRhADz33+zgTf3FUtBWnQT4DSksHsyR0GAiUK82oNOW/MGJJCsCWsGEBsyzUl7NOEB9zu1PQrkqrFL+4bD6SM/2BOaQFVZhJCyaXUWpmjhBUjOUpJfdFNfN7cjnGbnfrCURSiFumfCVJHzY0VluShuJ/TZuLYn3YHnt5P8OokXojuNXFXWYY9dhWooC7bPb+7bsrQfcbWrPsxMg8+OgQL/JhJHrQxBoHr72zBsB78mvTRlZ8H10OX13kl1/6BmS+i+3aeNLuX7C0JqaMKamQ8HyxpATaaX8GYgz3qPHKc+O3z4VVnzHaHdCKuCw7dVsF9+oHjzhnVGL1y3ZXCrchv8BabH69+D2/RtqihaNu+mnYuHOTmVIYZVGlblK2iDSrl8TcdhsAzAmyCCkBhB1xsap5h4Bh09HB99uxZVoqOTy9qikuQKam3cf7xUtnBcVOsYtehVdGcmgoANsMOKIqYH450xm2/9u1sVCOxcLP/cnFa/YsV4Qbxhn2ASOu9z5rx7OhS4t8TrVUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwdVaf8Lf+/mpO3lXd8f/l4wpF6ejFqSW0ZIu+mQ5EXkZLj+iOwnowVDqgMnpYjZneQp2Bu/o4snPFfaOrm2nNBA==" }, { "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAU9DQfBkBhR+iUr9GxTbNVob7ycwXFBHQbXwz4HSReCCX6487hT7JLOfPVdd7qyCeaGFRLssZkBw35LlN7TU7+3jbPMxOyvXwBmm2KBx6QR2xrREdBUXZZlqWgvshWUtDQ0Aj4C0Vwcj6H8nfb9oEVCsKPSLQeO9vhAC+n/hBIcAGS63PNrJOm/pABp0KGelVKEYSQ+AWz3ftbfqiQvVOXaqSt8OIxB9qaOBor3kQ6umTEc1Yxll6uqsTEVz1SKlNG20UMOdpQwjGzFi+e9ehrmSqUXun22UG1fIWg04emtgZAbF+NXSrmLZhkuTr6Bp6n65GmGLFYoy5YgUN4el4guV6VEWj+U4HC17O7bQCsyjz8blXlXWbKGx5w6Nry4M1BQAAAI/SmkkM5k4vLO5PR0Ph793f8pUZEC+ILXBXOTLdd6PhP5ky4/WcAgtGlHtrIh9QAuW1FFUgXyt7nkMu8QWkx7zDhcFjfczgrbRjPcjTDeRZKwLGdMgFw8geqHy1StLPCZcJJSKZJqrMga/H/S/Qw62bSrNJOI5SLQz3mtLQSbC0/A6tCuYSYDL4dAPyCd6sm6gNoi/vb9UOfmE6h6/Kvi6wayxUp/7wr5+M4Sfe5lpLW6OZCaJxD9eC7eSB6u1JVgzMFGHHuGihZ0hbx42jD4mFLwKGxtytWLnYH2qO8G2IkDVdx9eQQH/sX1SbCbHr3bU7mhf5zJQXuZAgChjOjRlpSIHWWVgrzuFt2WCGsGqTaszU/k9l2O0pXqdHm7cysZg1uA+Bo8lZ25lAnVMJEZorOi/VcbD//p9DxYXpKbGs9F1/9c8/hJkOcT4tIFaxZ9vWMEaN2Mh72PnwV5+Bg2s5HjgK1ztIDMOptjID/WDgjGmT3bhkRCpmsTJYQ3jdn8cPa0RjriTVoMzUMVWNYl15gz9Q+2CDtoy3+bT52psTQeLSpNo54JtxGNCW8ct9v0A/6mCf/9nYsJGIe86Xez9J+R6vAplqTx52lcqc6tH1VhVrzIEZELcslXEMJnjVISJjA/GHf89FTunxkopoPS0aBCGYlsfGAsv97340psZuF6j2xXJh29MLPj1DIQRoQWBY+b5oMd4nDemJuN+rYqp7oq1Q3MS2yug3nvVS0SDRHgBTK2AfRZRU0yXJUiyqJaunpBjtq/1TWVM/TRjsuKb0aJe6cFn14tnpunkyhx0YfSP2YkDr1WmQ3Pqhdd6lj4Iduk/Og+LAeo/AxaqFueKBED6ZRsUaNxPzehA5DZ/n6xs3AQv/3naEuSgmRhW2QF/L2eL245ePYLuLm30TdjdgFnA4DSeMbEjujtfq23iPEyt3m0rjTbsBJOr3FXzyaYbF7dnfjsIaoUu+Bar0A3OfXJBcX/3zBLUj6VNK+A0WX7ceBHWtjayVoJfyahmj/FYAFswYsa6PutSHgQ1mq+wD4vZIFqeMFelynXWWz/aGjOHZ0AHedkLfb6HrR7Nqd9HVO531fRDytf00wIecZcSPFvSLWDestcCMu0qxshKf2/t99OLMFWzwEtQyZNS+hUGf+1I5SgRMI7vY3E0SmA5jDXb/EfayXxoJgDxedfXXrqeO88zeFykcQoI0DV4Zts62pZ98rdLCzV2COQrPj6Eu9bLqTImHMWrAh1+tVmyotnz1fZEpe+8eWFznAL8C9l75eBKSl2aHTlrsUOvdbOlCawP4XQ0SK9jtaRzwSxE2ibBRvt1t9FGB36D+355NlXK8olT2DE9G5I6uC20KN1+StDZGlyn931FcziYp7R1Kc05wXGCFIGKlwb6kjyZ13IMY2mzP4LzWmycg2owURFgdXCL9+CUGO3NaBSJ9RzlFUunq7xLqNoHLz+PrNxTmKFFXlBVyGoD3hQ5/W7bfGi/C6U9pAHInIgjId+kZnuiS97ykO8+veG8387640rOLWl2QOZ7twQSfJt2ytvPi6gHPJ+5ENAxMzpnfakuThJZjoVtbfITANU4HRchbzlfYAw==" + "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAhaDaJtsn/DQIALcjOtUSProfA8O7Q6T1g1JjAHiTTeKNJtZZb6/YCp4dpznozj5m90+83gGtjZQIoOO8LhkqEZV6zGixOWAJ03ZoiDuVH6GzJAq8WTGXLgkBg/iPNbNxsC8vblH68YvgqojbFgUqY84A3ueP0/5KhZ7My94honMIT9LdkuyjAxear2R6XUoBPEWKab4NQmXe0z9RuxRoKSUhNCeDD6PE5ER/+anQD6qD6EKqSMfoncijyDxz3vsnGfgdZRuMRCOVg5RoLOaCmWf78cTFqgCOzVH7wGh+4ws55m174WZaPKdyMLf/v6iyibg+XP8e39LWadmpQoZi2zvXkGhaSw+ure2gb9BqM0fP5T3B/VUXzPexWarAQUYGBQAAAMKDF7Dblp5wSMp02Tdk9NnT2Y+I2YqK1FTGlaEo3TdzZwwuo1fDePbgEEBeh3cHhpLt0ECPZfAO277UVyqJ/enLHU7h1eUXqSWamWC6u/Ngg0V/KaLgusJHOB7MAtTpA4VMBtjw4JbtSfFY6qfflt9SXas7uMP5xo2WeRd5UuOZUh8bzD0fM3bzEicZMdUGnLn9pGKHoYyDDKhPRdSrhDFHXhvlHWfckZ6fwaU4YvlaJInbC82S/BVHdjj8doGHYBdCrA9RKP1okFNTCresl39xdbO/ST+R5kVK6GdKnA0fW8/CP7Dmago0EAOLAppqaoTUp2zQXfdWF71OfnvF0pBR24y3Le5vICEuQq0OkdSiD4fcWxbTgY3XlZcM744pHn3GOs4HIlYyF2bVMJ05EhbCgcXTXmj+RQDgATmJ6JK2Xhf80rnM9FC7zUcjdGeNCUig8ago2apajmjBShhOICetkQxVNgb9iwqqblLay/yNI/W04fdkyn5HjvgnLXXxA04DxddgKJX3eaQHVKfXyglHjsZn7zorUsVeuxYwtzEVVxn44T39ufsi1i1syyVqF/mNmJ93qOMxONZ2MK0543DXjBap5+2Gzez/9I3h8D3jnoXHc+N3ulilL6XCpr8X9XGtZV3quIGa0hrr9GsCm96/lroTjmw4FdyW7oHp3ypEn/3EgzCHh8AL9wVytRtHuUzbdZU8n80Lo/WtP+OB0lPSFz26orHR+x89r6FSiLjJ1+nukAVrZ7SD5G33uXdPRj9ftdrIsAhaGE7fLxPHV3aV0pLvG2qqMgGm9CF7du43RiWDIsSMFWmxkOXaY1MsT3BreYi8q/M4OF4QiWV+ppXg/+tIPRWk7ZAu/UI5bBJLlDycxb9bOBWOL3UIuA7GaniNK9LLCcgGqwYUOzelTNM8FVZ4dlD5tZTi4VUNFK1+6/G7jY2hAbQPLCW82G8ne8D1ZAYWcjwpYUYBPkvUZP6WfBptBInQbf1+UM+kL9AhXetUIzM9uWSY86qhkoqILytOBytqTjDjmNKdTtAAW/RDks3NiNSqQ7DCioAvzqOBL3vKcy7e0TR1PsrHpbgPDA1h157VB0RVzFekmYWb5+qVuZ4lU82hatkS7g0ln/kSzdRPLjqfEXiYOkOLfc0ggT1K+wlQgMIiUrwHCgdu9K46LdUzNlpatXBd2Bkou77Py1FB2AMtG4Rv6yUOkoc18EsyzoHH7lHxWWmBT/n4gU4ll/H1zQgJSBDjFqpC8vurgcxXBUIBLnsfvxeRkil/CvFhbPQKkzigA3CkpM1BByQFElugudeEW+OxXc9m54bWOL2QkSWcHO0MVSqN7EjjPTwj6imloNMYW64QVd6PArloWRBGTYN+Acj0NmdACpfFQ+w9HVSiUk3sWIV88XtjBhuMHlDI15KkhA2UjnugQrIPFC8r6wjNQiXK2xT+SMvFEL60Fhi+D6w/H605/ozA6gVO1c18MXw5DiYVtLZcQf0f6f7SFBjizt2NMp/1RhsKRdSyosV7TzHL+MgIFRQWa+KRGIGZh7s5hCdT0LKoKE43mSqxkhPXcRGH9rbr1k2VJ9/QH+if3JoKTKqKlS6+DQ==" } ] } ], - "Wallet shouldDecryptForAccount should return true for an account with null createdAt": [ + "Wallet scan should update the account head hash to the previous block": [ { "value": { "version": 4, - "id": "ab5b44be-38b3-4c52-8872-8f93b269b817", - "name": "test", - "spendingKey": "ba906d52efbd927f0be11d429358919ea1c87fbb7a4498f8e2ecd046a2793a71", - "viewKey": "fa278f55c53812b3bca2ddd7c0878050b6522f9f87288c3d03250fba9a5bf236fc5e71eac8694878b8c810d96d5c5721b9f1b3507a86eba039e893d09092e89e", - "incomingViewKey": "74d4b2b2c1cc39f4097b90c8b50a3bae0c85c04a3e588e922a18742b70b53304", - "outgoingViewKey": "7f7a01e7efdb53e7b4a12ca4664c0277429205c6b21e5532965f6add9967f36d", - "publicAddress": "74f189128d5f91e129af0bd86d986b948f21ab00561e7b47285000968b8df845", + "id": "63220a32-4403-4509-af43-7d3bd0f2dac3", + "name": "a", + "spendingKey": "c330e22814fad3116109025186b5dfb43024c56abd6265eba92b1aeb375106a3", + "viewKey": "ed1e0690b0d522b6d875ac45a1781ed269c85fa12e06d6c7b30bf9bcaae9ba269b5a6135b8e04f2f22ed949f7831e6b68a6a3985e518b2eb7151faeb4d2d156b", + "incomingViewKey": "0724670f39a3cbb2ff425f30aeba7b2aa2a923d0e693c92559dbde859c5e9704", + "outgoingViewKey": "0fe5cb9227cc49ba6385988f093e750a9d509cd9e4f3c5e090d4411327b2d06b", + "publicAddress": "ca61fe0a1e39bf3887ade3b530c8829f549b44fb8c84f5b5180eb20ba1140969", "createdAt": { "hash": { "type": "Buffer", @@ -7724,7 +6554,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "d4da17a97007808a389d636669d59efcae825d66cced3bb52c813e5dbf9b8e08" + "proofAuthorizingKey": "e0bf1c29b5aecc238f68aaec9f7f640c8820c4a5fd1389c20bf3422cd40d4706" }, "head": { "hash": { @@ -7734,44 +6564,16 @@ "sequence": 1 } }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:Ye223RdSL5Fj+ilufxi5m1pJ2cQEP04YtZP40xumgT0=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:VuyFBIxcZoBgF75z/D7ExBC3xFJKkzk0ASQhfIm5fdo=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717538998177, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAtaBpU33iI5phDHMDr379VGMo9mtOeclLnyNPE/Gk5DWD5XURR1gDcbaRZ6brJn/o63t1yIvzMq+mFjGuFOksTn4Vc3VUMFbNJcRAI4x4iBm1EqQTd5sA8eaSeb9lz0bMpT/fXduflL5uKiSHJ5WIlZ5zeaG3DVvH5eirAN9kJ2USZ9YJUA9SsDK/wJPlqVm7BL0/ph0kJ/UXL6pQawgLiHy7w0lu+wWEtX39Fc4nnB6ZsY6VSb77x5Jg4CTrX2AK3unixfUmZFKutH5d3DkGYKLwfIoIMQmTx3sqlbHHeHhCzAJhoLhYP9V3FE7rwc/KOjMitlzx3tfWF/loGkfrz7nWbaS7hEC1n60pu6SWwxQa1ntzfbFCpDfghO1CCWkrB1UDDbAZruUIuCLu1gqGGD+NLZMLed1KvcYTlcoGH6BHKmP576imyeZbSQHLkUWBBQ4pIAa+gfejAcE5asKsUt5MMjVa/LZsbt32mW707oF/ZLJgPOmMoFufLww9EB220o52pYQG1bGshC4agxJik2t936biKrUn+cSFDx7ODmpo18U4DyfykcxwCLbrcYkMoHX8HmyiTzhc7NIh0P95WpvuyUwg/UasBxNbxb6mHgzjck1zZQ8MIUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwVBoeIcfVogURg03ma8kl1MPPqMaSumSYTJLxHHmyK+f4idC1ofgBJ5PJsOd8NFqNgWb4t4NFwv0xkcV9jdloBQ==" - } - ] - } - ], - "Wallet shouldDecryptForAccount should return true for an account with createdAt earlier than the header": [ { "value": { "version": 4, - "id": "e4e8e6e1-2e12-4718-9113-80c11fee565f", - "name": "test", - "spendingKey": "8803cd390dc21e390155bcdb76703829be72bdd0d34883e9767b3e9a7ecaca3c", - "viewKey": "911c59f20fe23ea928efe890c6a3fafbc026008bc2efd219c61b76a2515049038b81f13675c60d7a7dcc024027dee03d3680e79c39a5067814f90fc9dfac5b41", - "incomingViewKey": "ee7d2844f24f69e6b7ac13b9016cdf6ebfd3281ce832bf9aae209485bfc02004", - "outgoingViewKey": "494d3e951b10776159af788756fc79169babd1c44674fd36abcfade121882735", - "publicAddress": "71fb7ef377bbe2d80361b858ab2d8cd2dc526bd5e1591f794e7d213f890d1644", + "id": "edeba17d-ddd3-4b62-a8f6-415bb61a8567", + "name": "b", + "spendingKey": "892fec6b956a1f4fe033efc2ee4c150d198b2aaa2a38a2578891d3f33e825ce5", + "viewKey": "dd03fcd010aafbf08ec089bce39ac5a5268cbe1298b44512c20dcef279af4bcbe7326c3cf6ae1456336249057896bf0368cd54118697a8b877dbcc565138374b", + "incomingViewKey": "27c2edc9e1452c416a8fa8a111041eb7c799fb3809945375e00ff72d5b20f503", + "outgoingViewKey": "1110a22e0daf369fae6a371d8a6ceb5ff2a94a529a83ce92efb2fb789a5819ba", + "publicAddress": "b764ef63638d4b4e41678d678f165f7421ae529483cf8dcc93cc2b2e6a7fdbd9", "createdAt": { "hash": { "type": "Buffer", @@ -7780,7 +6582,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "b8510ed4d78dab26b05034300b9192410c0f91ebf109c60f9d3261835d5f960d" + "proofAuthorizingKey": "4300937bd2f0c2d6e192b0943009980478a0b956b8d015862188bf1b4ff80e0e" }, "head": { "hash": { @@ -7796,15 +6598,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:CrdKzR6+2LH4Vt+MAuJNoo7NDeOEBXMeHt9dh/7/zhE=" + "data": "base64:d2LF3PDsPRhfgw4pk92GNhjzxFLj7EHvRa1ZMk9n/04=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:6anoi2aMrsOrpvlT148T0G5FALnPyYATB1ycah0mQx4=" + "data": "base64:C7zJP6liRkuYSNRLCQNzCjxt6JnF/IaQfBqoEotnONI=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717539000204, + "timestamp": 1718998729703, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -7812,78 +6614,78 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAXW00UnSpuO8qpo2Jyg3B/tpZdqG4VCXnE21y1dO3szKRW1IX+QU5tTiRscWoIIrLesCowNl+UW+H9Yh9c2f45nHsQ/UJMXrWQuHBc9B+SJ6luZht+E60UMdqZ4AiBwKyWpW8qcPo4W1huZaRjjgYcGA+0Hg0dPCOeEFKyc94wXoZz8eED57unrjvPC76XQJP8MX/15TOEHpxWEpvuprzT6W4lkHbFlDS59bzvWH/ENCnikKBV496LqMVOhiLvxPYChvccj4fOOgSYRLCIhYMS+n5S5XXC4xSI1JlVIuQDOcwsO8orzhg91Lo5Usyygox91+tBlHT5nUutTvPohwJ7xJ/1PnOrxgooYr9izO2BPGLCCdgeshE6k6nOhFoYsULxum0KMGo/tpWqRsKwQPglpenbW/M6IqBQQ2ZdOVoS8l647TznNHsmY6qGgThHkLS2yFqg1F3DDbTvSWXeUlA1pAAMF+TzD96lPWWJxh/UrYytkUjZyW//tKmh+OacfIaXQvCQ4bqO3a1jpQVS3vpe9O5Y+2DDBpdANSt17LPTG0/vuj/aH4E6EIDXjbMl6EWWEySd9l0M+H90qZ5yJq8OxeJj/ckuwkp+H+Qi2YbtMH6zgllwp3k80lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwsDGvyh14nM+TXNBMGdVKsRDxztaXSWvmYF5o2hMV46DtRKUv3XCBNq0mybrGxc8eC3YxvrX3YhCRaIVV/n2/CA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAANVl/XAxlKVm+Wiwz8REf6J69AiIlCz7H4Ad+OOBV752S+UaCzexMix+6s/AGZAz8j3o54DgtxE0PJY4RDO/QEvQCjmn9YRn4UB8k9R61+seihlTOQUmj4Ns65GwDNHJGe9YH6gKHBV7qdbhs0awIVKzPL1tumDJFuFEq2w/axiET0PUcySpClQB5/YaLtIe2S3Kj2O1T5zs+jh1lV3wY317cs0qWTx7RsEyJacl/fm2Yc4b+7MeKg6xqtgnRQ+6CWrsdnmEHgzczICUFQAKqWEIx2J32ax6aH+b4aEsUw+jeHgRoHhgt80m8cwdvxBJdfTap7Lqt7UNfe1G+0DRzb1LkgL958DcVzvyWokZV7OP0NoZsZlfnWHM+cPHDPSdN1DRNd5cyDgFyhU603ATIxVIUqYDDPn4YSUVjW5R/eV5huLBVTrCnLJuCdjb4BLiAdchECdfeYpZjS4pdb9BLba4iNX9l1Y0unT2PQoyy+JHwKKQ09Tk49zLBEsTwoAb90ZLNyCwHl3/hLbWTo82mpKcJyFGc4A9n6EDBYxJqxuwLB4CLfcuhLPf8s68LyusxDM7+XKmxAQqsClcG4HUnZI3BYBwUdsec7LjeE253EtZeWaAZulCiKUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwoEL4AQU3Gt96xS5iy2fQiL6tjZ149m5BPQTHdKV1F9jEi2Cf81MC2qrKfnftjQeqTSY36NVK03Kx4s6UROtsCw==" } ] - } - ], - "Wallet shouldDecryptForAccount should return false for an account created after the header": [ + }, { - "value": { - "version": 4, - "id": "11033864-5f47-41eb-b7b5-91b713a63239", - "name": "test", - "spendingKey": "a86f0e2bcca2fabd80d36b0fc5095523f3161e03d179634f2b8a8b0aba30edab", - "viewKey": "117029994f58adefc5f0ec01657d85e6eecbf6171186c493d16454b47d7f132d19992c4b5b8ba70905516f365b9907cfa483649fff11272f6600df2af631c1d3", - "incomingViewKey": "d64cc966ecd93fea10610e78803648f34b2e9d084ea9dd909e4a06b8a126ba07", - "outgoingViewKey": "851ee2cb193f56a66e242c5d2ba4ace7a653fb1d6b11476cc78d98e1e0d3525b", - "publicAddress": "132e02ec40e55f3f0b5e53721f41539e2559c3da3435a4128e2de74cc6ec9bad", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 + "header": { + "sequence": 3, + "previousBlockHash": "275222D3A20E9183D3589D087629D1B42A08E0045E2458E0CCF168E36A45E832", + "noteCommitment": { + "type": "Buffer", + "data": "base64:Kic4MpK22UVhbzQ1TFQjGvHy6UQFcn/UOH4etRS+Zig=" }, - "scanningEnabled": true, - "proofAuthorizingKey": "18813dfc16bf034f84e3213c1e7d10d2f5b86da87d0520e2e063b9c899095409" - }, - "head": { - "hash": { + "transactionCommitment": { "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + "data": "base64:LxSuzQfsGrSXBMhxKO9TeWtDNuM1DXwap2bfvFYZe10=" }, - "sequence": 1 - } + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1718998730163, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA+5Z3w4OeTJZKCQa6r33v8CF8cJh9PnY0WAW+cZeQqBOZqvw8cpGCoQYRP4qgcdmezBk/Sk5SlR3vZagEi3wDPAFeg539olCZWUq6MHFXJVyFzVq0eJ3DehjSTmiWZC3eXEpEzZQYksp+vnI113atPMmF07AjgaSjHI/JXjPwNPUXXwylSTAD6NP1NHuZl9vhcS5XFx0CPh9A2nosh6ZncRkI6Qce50Aa5W4MB/fLGYiiMcogoaLIno9lx1OlzzfT4BdEzMfsc61xJCYcpLSIMKjRMd4jf48jjz4enw5SYVbjMkBKXmvYHFZqnePQH8pYa/Y/Ig56ymmLXQSQ6r/FcHdhnqzUj6U0GP9Ng8RkUkZCQJXcNLqkv5Z6t6WJBoAd7M/kKo0Mjm1GSXJeZtkzvW0XtVyUNX03ppjeyAGf9AQ70CN3Z5WqH55J50wEYAvgDjFpOSmfhZAKDomOB+AYYa9Vg+idjMArvUgyDbgY94retXDxXa/qklt5tbFG2iI10iIH/huEhit2tIJanr6nKDe8ep+gngZOmeNR9c5yr3k1P33peAP5wfJt0DznjuPBXaLUjnlE6Hz7R9adaZh8rX6FNjb21/WY0cpoOmtjAhqLlcxvCBSqEklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAweBE+qXELXjPpXxgjOvQbeH2si8wyiXBk14rdaFRiHCPQZDFokcwwKTS8tILc6pR9U1Bt/0vMXxSHjj6gOUPZBw==" + } + ] }, { "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "sequence": 4, + "previousBlockHash": "A8DA1388883F1124C6C93B6E8190361DA577A134FB8BA066DE906D22A0B4D3D6", "noteCommitment": { "type": "Buffer", - "data": "base64:oL6ChZ4Ly5JPu2d4tRTaerDeczEoWTNMR8BPSdlyC18=" + "data": "base64:Eid7GfqN3PnQq7+9G1LjRGQ21NdwxpmeEx3Bdhe+ugU=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:7pYycxMVG5LSCil+Dr0/ffdPvPBzSKAsJ0BS7vt6xTc=" + "data": "base64:9QOVLzJMhQU5G8zrzhLcmf+P3vHPd+uEkOnRenm2+g4=" }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", "randomness": "0", - "timestamp": 1717539002251, + "timestamp": 1718998732418, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, + "noteSize": 8, "work": "0" }, "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAQWhZPSMc2rjX9x3Tf9wVm6W3XpdQNa9FrLxztLw4ONG1M+OLlAZ5n0ZrAZnbgrN+/RA+wisQNxN1gmrHoTO9GVFCyCCI0+mPEHK2PfdKhP+o65UGd70/+DCVBgr/b4rLGcKI5lUECt7nulxLkadWfuIjgRxOPUwunu5Z0KK72/oFDtDef3eUguxBTaQWwO4Z5tLhSwHD8ky+btDYX/4nahaXmataGge/xfnc858XyMKkVHE1KMssIWqATqHVz7rjjdNP4ViotOGY+0Y+6evAr9GvA6QYpa+cZ+NyJO0n/7qtx8mYb/+B/kKWggXOQdkv5YSemxqRXCwV+NYJ4LW4YflHA6iH2c1psJWW+p26lYhHPQXwd7XpRT9g2okgsaFZNr1QlrfNx/qG7pRBYYAIvoq1Tj+rWQ+3q8ElHbXDcxLaSLJgRk13O0deztI5l8tN/134hG7ZkXM0fVKfyfZ6ausW1CB4z6Ql+vTrD9k9IFIUv9qHMK2xfZzhSV3ar9cajxK4nqOPJ/73vnJm/knl1bfW5iSZu4cJDq6hbU2bLNx6f2vA4LODEyWUak55wgFel/aJ1BQreaqYK/hmRPsvDz9CCERiKnqIS3zBsMafmjmtvjgyb4OzRElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwnmC0egssnuPLB4XCtvfHNXGqdw/xPf3kURrN9kQeTSV3UQJ5XhZnQs79BcZ8FbaF4GhfxspKbm1mAmxC0ku7Ag==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAAt+cn3qJcRwVyEOKWXOMqawRL5Ttlk9enBHO49M0mmY6Izd+CBftG7BpsHRCWT6ff7djnzwE/OaDsavpsDG30CPYq4JpARM6WeOd8pEx4LMSGETDlHAHQH6f5ZjwQ/0c0bfjdBlAO/TjZgz2NMc4Ndgam7iSqro4dVDlx9WMEBRENVp2SfZALgPST1PN7xpBVdeK2qD8p/1LHkoxmnbbKo/14BSU9RXc0a5qYMZPanomhABqHckJuS/rMPDgtMQDLODIm35/p9aWv3j+SbVe+uCfmisdQoNaXP2oP/McxVh004Rabdr12pRgHfQSIWGs0BN+ImvAXB6slhCy/X9Ghv8Ff/W51Ugvjh2Bh5D2iOM2qN+fxUUhhpP90ROP9UZoGFGPgW0/ubCxPcIhaX33YDt5DtI00RWHCfuUIJFIpzhkne13sx4yXKErSIcQDjKQEbXoSZVg3siRUYx2xQM8FgltJW/k5l2VplSvhJ/xJ+r8rexWx7qmjm4SBeyg3/TBAH7givJIwP2/PRCw18Vaaq6/9g700XjQEvyAxFZJ41kXsJvkNjVZmxuMPnVasAfHy0zAW5yomA61xlAEp41JQdToQ+NwI9fr/9Tvy9EQVV/EvJPXmpyIsiUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw/EZca1Sac0WEAqFsXKvj5uB1v0QXOb+qET6fTEI4wCgkPFvEL2uNyBOfhLBKW4Reg/MwHilwcWHUiFEYkAanCQ==" + }, + { + "type": "Buffer", + "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAA7tp/JQGkCLvuRBrvr2P/0/shks3UYAMZ3sQBH+vgt2WCf9E0VXB9XAWBa2DbECphBYxQZl7vK2JFpTUiNVf91aESOZsUHj6wMn5gFwvNQ8unXIagP96Ws+vvDFAHtI9nz3zQDB3we77E2GAwuxHKCfedtl4MrY9rE+Tj4pgvsfkIPsXWE4DRftXjO/PpcKeCjPHqXtwlP4Qv3YK+8HFBBA2cUxwoKPCgwRULCgtYYMKCsAKVrsflmOw0U/zxvm8ch804VnFmvX+pS4jS1ajzj2Fx0Z8ZmYuk5ZXztk3Sj8KJivkzdg/jaipUrD7keOX15pOky8cQHJoTcgcawA3E8SonODKSttlFYW80NUxUIxrx8ulEBXJ/1Dh+HrUUvmYoBQAAAO3Lwnk0qZ52dlBxi6YP9mVik3pQNnJPhvqOvWlCLmOEdqyfwjFW3pQGxNRMek3YD9afRndo/I+yPaJP77/G14VBMsjhDkd/FGdV8YcoxyEd4H70QZDQBNlLGZCwNc32A5Hn4YRUZGPjklMAM7rH46VFd9cTzJvpWAtkfoXCaR+cngx2U7G5iuzkNc91gRw5m4XI8CAQydYtCLlQ6hWVZ0KhLGy0g8BagybsLjeT8M7HL1uDL1RuRA4F0inkUOI/1wxwgYjHx/TABO/u0YEGDVP2xRlSBGIs23GfhaNdKZ6udN0GZEwrl1dBQ8dGY/pJsq7loRp6N7blWPFsieZk/x8ELGWF74dpSW2WfnovnKP1WvljNLIupLqZACg8BAedkR3bidZzzNw34u1td2W1Z7Ab7+QRO9ikUK5zGH0uXjpXB7cJhbz0OA+hsoTKKxwDmQdt/W8iDni9ONKAqmblcjpAgE+mU9XBgHwMRZ0pnt/Ua9OWBI8j9/JMUJk39/P1K2qEfKux1brHEjifROZCeKrPrn7orjbAuSV/qjzO2gQTed3B9g071I6ppk7nzYEB/mhlqxvEojPLRNWY5tezFRMkZ6cz2F3hlwY6mZ6TNxBksvSkcpj1esKHlLok7hjKiQdsj+5RBBbNIiKC2ytTL+g44S9zliIDvFroJYIp5AGeN42orbfej+0iuH+/T1wl4/i8CvqCLqvwT95jeRK9NuIjWZ5+OII8GgZZvhYei50AvVj08ueV1u1vv2wUYy4qPwQmaoIEi+BtPkU3wZT937SFz90Vr4JjJo8J2id0SbXoDtXxnqa7qLOMhampNiBYrSEoHOpqKXlIww38vkIP2Cr+/aglHYBAS1H6sxvImro8IL1eO2h9UDC1X4tu42iER+E4oiLpaYVnnHmeG0A8wY5iX+0bmqmbDL0ldnMg3ojQ61pqWi29ClYMhDnfjn90juYsEAmvHW4cwUDggpRLPuhs7zD1F05nu/ykZiSftxQBKxe+G89rlQ6W1fACeMFrQ6ql6KduInuxiKhTdLrIU6i9zW6IKFufCgrNXJH1TFHOEMzNXhNLMyIdP5U9b5tzHj9UoPDL9XwhTpdMZiDaCySoiSaekaqmcE3YBduOev/ZiaaK8VlC/kKko6ZdhXRNsKygc8QY0qhtZc/4enVn6FBUQEv7NSdefLt0eEuUk5QMEX1favkBCjItGsUXFgoMXyYEF2KEpySj6TCBQAmCVYTNB0Nz2U98ElMGZuYSJj2XwTqmBheeDUihUgsZgWy59ikDY/MDGYrojsvS8C+FWOOkBjTX5PmDniXt8twrMJwPMC9wlKYd1p6zRxM8wQGK49hwVAcFuoGjtIUnEIzNfFGTsPPywGhJvIg980HgYwA0mVl79U1onk2NtrlVpWbqzRHdwTxkQ/pGkSJfo2n59PH7uV3hkI9xUmuyLv7wGaEUHoHu2/oztfGJSYlQc+i6MeXh2753uhaq4TeCalVdVDulM16dY3TCiwSTIVTvLIoGGTJk6C+ycweal6x/NvfrLanTyE/5miDHVpta6dc3NRncxezyGXsZ2Uc9Qd9m8atO1vgzBfV4agGp6cKHPXt6Cw==" } ] } ], - "Wallet shouldDecryptForAccount should return true for an account created at the header": [ + "Wallet scan should update the account unconfirmed balance": [ { "value": { "version": 4, - "id": "22ad26f8-6324-4047-94f6-6320745e7664", - "name": "test", - "spendingKey": "5f65a8c9dc83cafa7c62a9831653e5c14131dae0c217cdc7409835145ceaae3b", - "viewKey": "666f5d9dcadaf6c5395eb11cdea1650879e36783dca34683635e33e6bfc543ce4413114f480e2abcbd0e0910d2fcc37a6b042129458d282dcc3477620c24eb01", - "incomingViewKey": "31b3d71788afbcccdf4e8b1aafaee039bfc41bc768c6d9dc04c1774f671e1106", - "outgoingViewKey": "2f1b93b172f76c3cd8db1e33fbdb4d09ad2f56e53b8756496201f8e69f94cc20", - "publicAddress": "3c0e7e7752ece79518668342353f58c0e2ded73c5f8ff59bab2dc934a650dc5d", + "id": "443cbfa0-dc5f-4a55-abd7-188b9989ca51", + "name": "a", + "spendingKey": "f77400ae038d4abcc3ad56c5656736e00a5705e92727b0bc0336d9adbaad4c64", + "viewKey": "1a7549089d44ed91832f8c04a5a2251c9c39382dc2bab1e97ff411f6e07e57347306079f43ab37c1f0b3d6f3b8ceae98096bee94a7f30aceb43ef2d8268b9cab", + "incomingViewKey": "cba672f8f62be3a6063a5ed589a699690899f458ab51fc5f6f56994a23126f00", + "outgoingViewKey": "5a30ff3926020d28cfcd71f654c9a60a0de2802143ac931eb507d09a6714039c", + "publicAddress": "0b1ed1b1bba0c28586e1cd5676452941716045048536a7e172861aa6ef2e99e8", "createdAt": { "hash": { "type": "Buffer", @@ -7892,7 +6694,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "a09f3fc03ef8e59fc4b7f4e5c90f920ca113ccb6572761da439cbb0bbf6cce0d" + "proofAuthorizingKey": "ba62929ac5412d6e59e72a2047916f3e4bd2332c4f20de1a9c17c1dabe79600e" }, "head": { "hash": { @@ -7902,44 +6704,16 @@ "sequence": 1 } }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:neId5as8nWnh1dkeNwfT60oMa8Eimkjep3sqjDz35Ew=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:xfYXQYm39V4hSkYFeMAWOI25Kw5iUTCY3bRCmb+6D88=" - }, - "target": "9282972777491357380673661573939192202192629606981189395159182914949423", - "randomness": "0", - "timestamp": 1717539004596, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAQAvre+kQkIaCW7kQ1/ZTGhm1BlX0yfJDob4ZNUEVx7OYjqwI9S9DEnnPsw4Ui6sbYbhQ7UK96yCy7MrcLvTogCAVEcpAxvhwSh6KN9aHglavTbp2dovF3LzFHVKjwM6W54QY1/guZlg9jsf9vJq2to2F6KIj0iupDgtVWZL5ZykMv1/4c5IhCAbFgxCbbmfwn0VHec7qsvoID+NTnxbozPPgDQuB7ymZrAafEj0zHLOuSpk6bIHUYHoTyINvdI82QcfNpGVI4j3bwyQoiHYoOWNjBsnUa4Fzv3L7uFAQ1djs8Z1eH8W1056B/xMzTf8MZUbA8s2DjrXdM4CSRubuihtUBdtzNyUXhCVDbEprbHLiehM4RAwbjqo2B8WcLPxxRsezfH54n+iIo60PEnjm1/eynKLAB2cBzJwUTnb3uzfSIb2M/lJ3S6RTXWKdIlZgKqdgTcEFTil9l8/5CV+kuLnKiUvvd0tzf3UxXIdgsyEsat27IRfRTRYAbhI0fWbIRUvy8x7dpWO2sfCEHIJrITqsk+bdztvywVrEeLkhewdJ6PrX+6j5M8Vr9Vu8iS5lLbSRUCqDwNTaTnRkASmqeTUcPzGiVQbMmt5UhSE+O/ail/Uh7rDqpklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwyocN2WaEeJlxznepvq5LKiCOcApaZIhLhZNlyTGgi4TQZpRqPbpTMB8/aTS7bD7ES+29kxkULU4fhbyekZinDA==" - } - ] - } - ], - "Wallet scan should scan until the chain head": [ { "value": { "version": 4, - "id": "f0c05cc6-518e-4a60-8c0e-36b624461c02", - "name": "a", - "spendingKey": "cb469a5b96113d3ce511481ea95f5af796246c8210ee36ce933459f20f9ad054", - "viewKey": "4ae13a3528b39c2159b934eca7835ab728d2cb6cf053d103bd90e2c1bf8e1b6c3358415e2dd8955c9c35f33fff9888a0d39f7168ccab32b57b8443adf2d4d5a4", - "incomingViewKey": "ce8f3645d46a27302d7cf8f50b925fd6093efa5d0fb1281f2e05a6f299b07505", - "outgoingViewKey": "cc5eaab7ea9948a69faf90cde50994cf5229b3d1a4ed346009895e4089b3deeb", - "publicAddress": "58ad1cffd3236ef303e9543a48ea92784f46233e97de4f739abb432860bc7244", + "id": "e1f17f03-19ee-4855-a041-7c20c3163504", + "name": "b", + "spendingKey": "e1a1fca44e6fb879b0ed333e17655940b0bd0ea76d29c7471395987574ac4200", + "viewKey": "eb24c844e8cd929092d85ec5b8aec0ab5ac0f1e98e8387a2710e5c62d06f9a44d1d2271d657c5a11b6efe8a7977b919c3acfc0d52b5083a4826d93718f40382a", + "incomingViewKey": "a090e822c984a1e0242eecb707f95a44c0a6e451fdf71b46118367b0ccae0e03", + "outgoingViewKey": "14b7d822c079bb399a090253e1acb60d7fb34afa929b5fdb07b675b4d8142b29", + "publicAddress": "98157d7ae91fa6b63134ab69de8b2b846e46f7244584421186a60db6d707ff27", "createdAt": { "hash": { "type": "Buffer", @@ -7948,7 +6722,7 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "2a24fb483ca3e2f2d5e1dc91513da2b6026b6c89b92e0fa154e9d05d4fe56502" + "proofAuthorizingKey": "c44b0f397080feeb29e812bda53ac2329a176f0904cf6c15cd68ece8215f280b" }, "head": { "hash": { @@ -7964,15 +6738,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:/00uglzCYK1RtfBd+WM7QsY8fcMjUE+tPuHc9GBkgSc=" + "data": "base64:JAOuWmBu/5e/8/RiCwKZWAF/U5Ax4X/kpb9MJZRh5wI=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:y8WerlC5JWcBLf17AIqMoKxfYsNZvHczLdfal/RGwgg=" + "data": "base64:3TdAabY4irz2YH3Y9erSn7R0uoEZvf8HriiPOT6H//0=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717539009281, + "timestamp": 1718998733489, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -7980,48 +6754,52 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAUxDTstYMuM9cawjYTcco2Rm43dbO0mhx5TdArJFcC62j068X726p5jAwYoKZoZ67qvXlWAS71Ezaug9k199xVN7HiZbw8hIhr141W0DWZoSY2A4ck9/j53UHZ5irLzjOGtRLRg3hzUGfJcUNoBsxjS8CwAliIczo9xJUlhnCicIDlA2uHFPoYWUeAzX2uACvsSpb9zdjFdoAtcEXPTpj05VFac0duw3kjHmDMJm1/beCyZpPVZx69pjDzSAp/Qlp1WsH7TQxRGWGvdy/oYRXFsI0KtmPhVKtzPAyc78WNn3h3En5gGcnzr/LHT5DUnTeCputh8QgYsJ/PtCX+wy/Sb7L8UnZWkqkXBhe1HkRrOYdR0VjQuVztszS915xO/tKwTQJPQR/TZwAnQu4nRbcDyVZVPeUpOaWGqE2WBcoY9tNZ6vcsE5FMg8E2P3G1BCGaeArJOqbMOpNSdVskpXDqIWWQhN3vR0t5dFnu08CYF5fgnPK0Hv8BbswFQCjsoV3ciltsQ5KKOXHIuCs2kT7FwpDQnR9Hy5NySzk3QKoP9YZTqRmf06iCoxsqs77Rv+n0VlgQGCLAeeK+YFAyeUab+NNybVcVXSCkTA7y3AH2JFlTiL4EXGavUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYS+KKDPB3TsOXtkukoBjlMZZZ8q+QpNVTZz/leVM0NGVpTdh0RUsbf54yQbeeqDX7cFl4dY+lsZyR7uj7s7DBA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAANJgWLJuWiGs1umanMHj1+nIfhsODke6cO7kFZb/Ys7GBwbgJ+mA706Y2OsRUHD0ir13Ewe+vE6jD+Bn0xZsbNrR2fj1mTBZ1v66Mf6WnKhGjW/dGQr1a0Z+pdp3ZkZMDs9ltPB/NGQTivkB1dP/BZ5yiKQvtrgwsQW2xNhzCq9gCkdalu7CDFX5l9FeJZT/OATXmXE6y+9yFlxxCgtGqIAFgsii9n9VMwUIQotsL+UWrATdSfXEpRASrEANGCzfOPXAeRP+q+yXpVWhrPtOYSgTGYrTx/BdMlXfZVTyBgh2x0JWIQ6b92NO9w0K10LbM8ukfCYZApkKMXv3dCfZz2b4WQtpsU+xsaT4fameg1JGFbhC4DGryDJerTcdli0sD/xIBNKiWj25hsrRN6r7uAEjfJEreSBx36QWWWg0E1WZ1Lr7pfEN65m03PNOgTdvulYzOVwshMIRmpxRIj85QxFg3QKAmiYCstq6b51MHhmvtBrnEMdyCO6xceQpqM5SbaN8CCwYn+GANpJT4UpDThK1ywk5ScMmIRU5wR+kiMtNFc6SYTMzqdFfgfe2+VUZ9Ndnkw8FyI35CFNoM1Gqpm2zaL8BDcrE+JWy03dEku+bQp4qXq5oExUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwEirJVXAhrO/3zGPMr2Bq8cDxcXIIG81dg2J6xSS3qFYXXquSRWYL3pthOXm9H33iASoggKbPsj2tLHZFEm/HBw==" } ] }, { "header": { "sequence": 3, - "previousBlockHash": "8B89B43F3956FC769F957449516569E48AAE76C7E1516F43781F37B40DDE6741", + "previousBlockHash": "86B25C4958CEBDE15BC9CD38D25DE3A9487F08DBEB8F7057B885D31E51FE9ABE", "noteCommitment": { "type": "Buffer", - "data": "base64:uo9tMuotmcfPO6KvlL6q4CnesDqEYlm7/TFnZpHWTFA=" + "data": "base64:2UkxK9N8+IJzC7/AFAF1+lHXNakTzXfVG+i9iRr2BDg=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:mzGsYnnneE49w7ACX3U8Fg0FStW0D3fmPnEhZXI84xw=" + "data": "base64:yMOSFF+Pmzh+4AGWUj8UXhnXrk1ldk8xAO6Gh9iLbw8=" }, "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717539010301, + "timestamp": 1718998735748, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, + "noteSize": 7, "work": "0" }, "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAm6ILXOTB+khyWx3f4nNeMYILKANSUsv6Vf96Stg+qmaKLl90+O/8xwrViZfXoKbS4+pokN3N+6CWOVSEgGQ6FXkV/iaNvSnGHZD83/Fg1s6PQm3b6eRg8UBbAU+IQi0i8iOSlVro8M+XZ8ChJrAqFLvvVEhh/uobrCCRYYqrWgwAi17NqR0zbtGA/mLbW1z+2ngREYTnGEfrtjo0YK0rTlzPJkxF8bsQu5nn4I43XZmNJ64fmHr9lIWoed/wQ17ErBDexmCW48XxxXtHj1TXXyAdTZ8br+wXRuDA3Jae+KM5fgIRfoUIM4/bbzt5bcZRDxIGkygVw8PYAyjbhtq9nbA3lv1U9+hz6JFp8XI0ZxC40dH6wKEAMJRKwKGjeAlUphPSkVfDRJnHRmmIrAkPJxtKJKVQF1qTpz/DRzYacDQYfdr8abQ7QGwltMhuWeESYKd5B+UxwyL4zy6ew8MRq8fDEQSnnnVSWsYfrdGmoQcXgqh3oomq/rmEhgsemhwDJ/rnEeJ2MTESpc6n1xV3MunG9jNNMCBk8rzZwHMZmQg39j7mydsv90nlZJ5gaGM5k/SOAzekaECc79++dwkUiSebWeCMSb6e0fYS8rWQ/I1Dhn8sBYi/W0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBDKVr94wIo44rKdGWzeNvRGHkMwEMw3uAZV9HBE6mG+KJ30/UMOSL6QMU3GphsQH7P9h2ZSKhiVMXrikOgo7Bw==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/2vKiP////8AAAAA+y4bbBfJu5rxeGt8f4v2JdCYoEQwuO9i5ncX3YN9PeavmpTnsGlqdmdPYi1GAnVSuwu3of+lz82jlIGlAMzXln4aYnGa9nzGVtcEVY89VMe2syy3nY6jc2RbTluOPAnWFjPqwnmEF2SK9mXQPe/RI4xQKz12rxbYjLMJECkS9jwMBxjtxwWZamlEOR0fD3wvYNN9dZSSAywxsVuorYhFx+pw0sQSiTSKUrENDr/ysia2hSGJu6QCo/OIqQc7tw2bCcR3ukhPp/IKDJXy0qkPpRvDPjhzL3qKQh6CJTP4twVA/T3px1ERow8pPt1mh0kWb7usEDdTNuYmVUqjqryG3++MfhdG2wkTuvWCH2AEmzf+W9hOpnI10T+IkP0EZoQBtg3l7GyQ/hgadXKMPTvfkfeyGP0SjQX7B+Q8z6kRWMefoLCfGJogfZ9EQR0mEA1xsDgLbQ3i8Q5JqP6BD6FS4eVKi/vi/qtJjfXbIn2t29pF8nGt1PI7WZDVs+aC9IXjF3ueUJONVm8KOD9bmgZkBRsB5ekI31dPsx80t1FeCePpEkL5QYJbUX34+r/MBDFe8ODwwtIx+/VOcXncsUXZz0os/gzApgqcyby9X1kab6gmoRX5XlEq80lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw3fD2hzP8yNJGC1ql1ANxzu9H5/UpQY0fQ8+9O6OFxrtDIkzuZbBCyvE5FFSg/ZnQrX6FYA9SbAFMxmSyaRUVAw==" + }, + { + "type": "Buffer", + "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAqWZmNvRWwun5hB2oWP8cbPik5Gujgnh+skmTGFMPl4u4UGj1JBY8uDsVOzYgKgO/KRoNy6RzudW+2/RZeQz8Z7/UD1S66YRbTK66ca1fTcyEjwxZuseoaMJGfivTnNTaBtcoiWkYUn/vNpWii76sG4o95pPhlxEZ1L1OvJl4A1gEEq7OLKy3xf6OBnMUJlN8OZC/aqrsV6BCq6UZa3SFEOqa3v9fLSLklarZM/CqM5OVDX6MWU/QAIASRwG/AWMDea/iEAnHEC+WzshCL4LhHAXg7PdCnnC9chJYzcEvNeYld9aXGVQBJvbSHbhx2cvBPgbpsfib9p8m0uFJs+9YyCQDrlpgbv+Xv/P0YgsCmVgBf1OQMeF/5KW/TCWUYecCBAAAAJ4acn5O9z7n7PyXoFg1IcXYHJR04zc6JzR2s9hPYkHRWrC5Dv5uDDlrJyheqnFnGYf7kK/qMh03+p0I2XDUA6pv90LaptdIoTMRjgqFYBWw9Fzh1QUhGkttX4qnM19TBoGyjSkqyEeO2D7byk/LpvwUaOYXqXZf2LtgsYAZTXnadjba0NvB8hTDjDMBWEl+v4WANXX8pD1r0TPhc7jLs1dFLqio1qcgDfDtc3Mdb/5MCc5BKdhrfXTv5tsbFgKTmgJ0WBwpnGLxO+DZS9k14l+F16PfVdXfES1QqdgRFhg71FtZ1GFA4Lav9nVr2o7B6ImaQYBxa2mKTQUiPizxpVDiaaPsUq/9IrskpLG1lKWvETSrKc9oNV8BQrCw8EdCutFDRRmw3QWOAigwVe31QNoEY5LolXY07bjiLklhJyC9tELzY/EjVywoPrYHAkQo5eAI/9hFaZIPOSYYb5nOUltGCFHK7sq7a4KjdKSWRSde6N3HgZ+ATWtx9ZGvLCNeyApwhbobJhPDAhil5Vb7080pm5yfJ6eYpmxrRBy2wBtswxVjmpt7Ly0A+QFzAnwntF/ocyqq+lF521bnukBSstULxJtpGENAVr69h9ICeDFrwCVbwXW9G6Vf6/qUZdx+reWtACIz2Ii7+pkDOG1iKZg5Y3Ux+sKesLJvLKLqMl3+hAkpe/PhYd4A7572P2768CG6RpolNhDUy2H1T8Z7/F2p8x2Kw/dnAsgu1BfCvARfdZYZVhHHp1hBLuICHpi2Izb1I2CBtFlAfmAmREZDB1emCfyOdra0Wzn1sH1MgJAyTuzrChSNFu+AJO8WuDuyNsMnMVRPTQi6rx7smsiqbmgyXjEMjNd2oFLU+1niQqCZcXxX5JzZsy6FvQy0e1vEzaIaYjTOijyFNw0jjd6drdLGtHa7PdWePXYeeWJyJij4i0bE5f0ke5MCrIdus0Hfn+iPhHsDmVcRLtynfcLnmOAtPSI7DZuB/3g2eIlfmcxKSZRYFrpYse+kyIE6sjsYyx7SRlZm6pakIqHwIufXU1mKmeXEjHHyfA0/EomcP5TaVtmtf6jJEpjVuA9+8KzfvSqupJIiDXCGs+nahT5/CbYTQnhZLsmQiq6mHlvWGb9hfyLimT5HsEd5kcXV4FCNeqsZCnT9sbJdBb9zNqpS1rv3RQ/fotNKBTwwisK1UqjzjB2LnewSYeaAVCGDMYjzA80qrc72Si/5OYXu9hUzj1JRCOuOGpKJsZe0eJwUTzlGyJllbuRZaJKC5mjCXTzl6ihQ+/iKg4LLc6jC/ewkMUoWgYWgIQBahy18Nn+2rOWZA7ciABWpD0iYQn7Qia9qI7MCJAChYZOsjTPKnmwFli0ESdh74ABfKO4t5GHTmbroysAH5SUL+B8rZAzWofgQCeFwo1sSqeg4D33OeifUDPQBdS4LjchfmYoxSxl9/Eidu8yoFuGbAWOcH8cwiGGtH85P0dSXaTYXT+wjZrQhA5psNg542svtU754sGUkJRR+UJwkSFg2HS61SHounGQ7niB6EgrGmiLtzpyQ93EmLhFlfG+c08K814r9QAZ04m9TCrAN46e7tvFVomOEmP21Cw==" } ] } ], - "Wallet scan should start from null account head": [ + "Wallet scan should skip disconnecting for accounts with scanningEnabled set to false": [ { "value": { "version": 4, - "id": "fa5fec21-c63c-498a-80bb-5919bee4f57c", - "name": "accountA", - "spendingKey": "2089eed9e71a6ca6ae3fe89417ca1f42349aad0b2ed0f1da13fc958d84032880", - "viewKey": "5dad30944afa1173644eafbd2e4938cd3228d3aaab8668587a2c0ab0cf42cf256ab4365696caeb17efe20b909935f90e5baf6d84b2d70f7fda4a15440aeeba5d", - "incomingViewKey": "ed5859e15f2ad48861e603e4b8fcd74cc22637a12e9a4e3ddd8a4c5c03908b07", - "outgoingViewKey": "6ae21c18dd5b081ecec4fe73fc417ea7c83213e218403b3b80a92d8f0420ab20", - "publicAddress": "b6a489a014a64d0113c110531b99e19927088f301bedd11034121c28ff977848", + "id": "d01c45d9-1c27-441e-b784-ff8af537c9e0", + "name": "a", + "spendingKey": "8f9f64a1eaff9f7feed5244b5356aa61c8edab7d83227136ce701e69c1d0a2fa", + "viewKey": "9ca7f43bc49a93232132fb6eff5762dbe7ce62cdff57a3e276a3f3fc1d68fbed02965c2a565c2e807f24fe62cdf6ece4a44c55f542f43285f2cec5a92e3bb7bc", + "incomingViewKey": "ca7688753ac80b4babd412c1ac2b39148856a7ca306a54f361782ec9fca2e604", + "outgoingViewKey": "16fe0cacedf66074cc835585b159b984784685a157c2511394c2ff5cb1893f3c", + "publicAddress": "9075283923f7eb216acc138b4412e2b78ddc99f48c9867d516beeddf84e0d872", "createdAt": { "hash": { "type": "Buffer", @@ -8030,7 +6808,35 @@ "sequence": 1 }, "scanningEnabled": true, - "proofAuthorizingKey": "8083d151507df2903b0e39ad090c3467a8035e6d230348638b5c8fa4a53e2607" + "proofAuthorizingKey": "8b709ee47be8e24d64970d6ac14fe5e14552af8acec8c486b3cc69f0313bb802" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "value": { + "version": 4, + "id": "66ac2ad9-775f-4674-b49d-7277abed0d18", + "name": "b", + "spendingKey": "7c524fdba59b45ca4c01b65be6aa1c4edf287788bcd7529f2da08cd786ea56e1", + "viewKey": "e76d6d8128c44b4bda9bb58f2b663641342fefd79f98dca8429046653aa2ba44f36c4af64d07a47d38ab353022697331e19b8016c850ea7157b640680e9ec0ee", + "incomingViewKey": "de480528573e53b2139f742f194cb99984f10df212edb1e6590c9dbe8127dc02", + "outgoingViewKey": "bfa120aa34568cbfd328b9d6da5057dfa6308a09ab338f0c1b7e348265bfa8f1", + "publicAddress": "6e05c6c0a4eb2a30a09dc8806240d36a766c2fc3b672857a9f465749cbad3bbe", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "8091bea3e0c5c249a43ba005b99fce709dfe1505bcab213233f8e09fe2e3b005" }, "head": { "hash": { @@ -8046,15 +6852,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:RpA3KWtFNM2ycx4RqUuxT/lTJXhNsgSSzA+s/MjUzFk=" + "data": "base64:MTg/aR9gMZCMIXUUkINNLEVV+pZz1Je97ekm9yvSsXM=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:kC61YxPC+w4CJ48uIDdKrdlZvlYygzEdESlNqDfQj+k=" + "data": "base64:6bRyH7X/5l6rOOtDOqQh+aqq5M34RnhIQ1dtomZx/FY=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1717539012639, + "timestamp": 1718998736844, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -8062,53 +6868,25 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAQtcKGcQLA9vdKYUbfiboDRG3yNKRFR+TYJ/xaVIdd1ezoqSGFv7an9yxIx+jZqggiPeT3jBvxXc5dkGCWtC4aBBgms+g6U1iPf747E8V8tuxabDVbG4ZUUH8z2J7UfO+Tkj85q3VZTik/LNZJMw4PqM8lNixWgPSf0mbNeJx9LgQGyfPwlFJi4r102u7pUTy6NrzD4fJUdMVJ57pI2sHs+luhZEiNy5G/PU/kHlTUGeqWGpWLyV0X8HcrmFL1yUnvDTqXI5wGVxlRL1LKxhYZ9/bjZgvzCQqJin+IYLc7u+HrcfezmDEmdPUsBnAi2Jsx6k7Wpk7C2NAmvFsTP1UTBVMQ7yvBFWuBr4oIF7X+iEQPm6IxXQhkeopc4V/UkFTbt7GOPPPxivTYQw3RGo2eExeyTosat7UvwzZ/OQxQNjSKCa+U3/G2isyvH+rUqDU5VUKTXgUN+BB/4Ev49nu4dSo3Xq4S4lEoGz7QBaLscCBrvTKnHU+GXpNO/KpgZmkMWV8P8mgDBLmmkeSg8ZvrHPOHwhT0+k5cdotIR8xJ0NfAmZ/J3v3tjct5axZZdm/dymwlxT9OjxbNZBWQfpgwghXg+KYILEMVMVDhhQJ7bmDT112vhoDzklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw2ggYFzS9r0pWoLNkQXMT911ZDG0P+EdOhVbSNjRSS0tFLid01pUX8beOMYMESLQDWCpl9awFqHDFqJGqWwlRAQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAojYc9cW5YD6f/aC9RGr7cMD5+abPX0cvDTjbIYQ70Bmi8U277G26BgwbJrhL67BBLx52FXA4vIrElodtFoXpUNtM3jE8clfPTgpjZ3mbzZWWfiRWi1pRlsleMs9GH03VMPltTl43JPVR7nZPskdUTWXSwQL8etZHzGYdXA7bwagD4At1E5cZknuDV0+TO2o1dbvwHUG/hLnTc/jughmpIbMJkstaIL96XajYJ+w/sBuDujSehZIus3hGf/kWHlsUz97iCSfHoU/VFe/VyHyH/0nQe0sZnKwQY6M5onFLOqupQukswdz86Cn6uzVK5xJIn1AqMSwIlHebVe5w8bCEOhtd2QhhwcLP/s+tTS31vJjXofak69IziudGgUf00GFN7sxVLPlMV6tzj7bkVV55fAOijqmYT4ojcGQbGr9LdI4lwNvr6yq9xeqZj+4Ok6ZitG12MStvsxZo49ObhkXGh+EVd05+eL6uMpOK/MsnQ7PSQVEYfEA4RFNY+ROCjNgkFyFuS3Wgt76netqrrgFzuvsuW+bRtlMS0xpALhvtJnMyLVruXQ4Lb3/NAiSao21ch4IDNGkRmLwmxeXqyG4FCGmOeq38eQYFwElymIKkbNw35mineXu2wElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwLQADJ0kFUwvmaZChkqEgsM2amGHvPB+NHgy1y2uVuJ0Qb5C23ATbXfWgvKvy/Zz1XEck4fBw5N/vBpI3UJ01AA==" } ] }, - { - "value": { - "version": 4, - "id": "b348877f-52c4-4880-8a4c-1c41f458c46e", - "name": "accountB", - "spendingKey": "fd92f9b2fe73330502288db549743d676172927667cf615226256bbd6f6a2a3d", - "viewKey": "dc745ff1c68bb7ed051719541823aca83421d6a55f23818aaa692b0c26b85a0bc0931f4addc90bd6b9c8c36e3a6e792c7b6c13b2312ef35ba95cb54c73846eee", - "incomingViewKey": "217269c4aa780e41dd102d3bd6dbf3a192a637b5cebb4ca730be5b8bbd5aa403", - "outgoingViewKey": "8e6945c8a702223641536e38de45e76cd64ef9e8ce64f2f901ce7ae8ca6e8288", - "publicAddress": "b85eb15dcc56677ff073dea9ebf79d284f6f34a83466dab734cb3f5e3afb53b6", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - }, - "scanningEnabled": true, - "proofAuthorizingKey": "46af1b8daf03a4f4aee94a987e6d590bc7ffa7cb6781ee82b5bae126e53c6006" - }, - "head": { - "hash": { - "type": "Buffer", - "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" - }, - "sequence": 1 - } - }, { "header": { "sequence": 3, - "previousBlockHash": "09FFBB8D53A173E1A982C8ECB57ACC9651732D0BBE0BCFF971F19B097F9E72FA", + "previousBlockHash": "5A1DFE92484FB9E71E8A586CC2382906E260D9077C20C1300942A9409F020952", "noteCommitment": { "type": "Buffer", - "data": "base64:KP/vitIP8dVAxPlWs3MzTfd7PzSY2sKpfA2IIjVDkh0=" + "data": "base64:nCNnG+aGfdXDWeaYKsjwyu9ucIDwL/+HKQ6PV4Gf2GM=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:RKvoN7dEaA18p2zAP9UGIMXNfnvpA1Wdnycb0NaXCPo=" + "data": "base64:ayPbzwWzU73tXogX3NQtyxd6PqidvHJnHOnEjEn12O0=" }, "target": "9255858786337818395603165512831024101510453493377417362192396248796027", "randomness": "0", - "timestamp": 1717539013985, + "timestamp": 1718998737312, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 5, "work": "0" @@ -8116,7 +6894,7 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAeRsNEYt3ZwfhpUmTnlAoxMI6+BpLof6oRfHanFWaLQqWZCetZjOwdBadG2PWNRfhOYCj5kCz/FYXyqKDMKkTU/gS5tSiLixEqeVWt+LwUneKKoyXAeZ1OKNH/obHr4wFzvBwbPZ1/NhhgsGvbcBmENXpKynPgC4At7PCqBjLn6MS+xh8L8JI2YI25c2Jzac7boKEO4SUJWqQjnhT/PVNx2NJkd+ET9pPxDPxVJ9qaEGN5lmieKU0h91OfnpxUPWoHvbHt0fyfrEXLJOH2IMF1P6q8YPzmBNIPE9uWSh1Khrcv2cKbkul0cpw3Nt32rShV5kTakqHe/LipBTzKGmRSyfMk3R1qvkdot4OrlSGrgB9MMJKLf9yjvMe2Mq3p0tRzViHcNTZm4LWDwbaG6MdKaHz94M4iDqzrLa4lxGQaNoV6qLzRvvUtvliIlUh6ZRyXS37gNlD7Q7BNWuGEMsDmjy4xt0/fNsbO14ZaTKnn+EPbGECCOoDKX1yDFBlhzCAOEd41+Mi1AT641saSnvO5sMHoLy2r4x+lrDE7qyCL/3SClcmj0PFbi+CjPhSJn40hErDnV3//Q+X5tw+NcSBzwydSpdZgwwRm38Y+h3u+IsG281wffNTR0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw2eSVCgULZgJi31KzK3GdX62J/O4YGGwndBTL55y1Qd0m4Jq3ikdnPKJCLa67MczlYI9hwYzENBQbt3O3NOeRBw==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA4q5cDD3EIrK9jOzo3PTC5uyeeHAVhKl+DEjb+FwJzjqIllvMNzG29ysYAQGkeN6Qo9SfgtWUbEItsWGmIBdg3bjgKP5hS6iz9gKzkziLaOKPSn61Ins3rPBJgvdZO8YMKnPNqTor47YDW2xTnN09+SAWW5paLU2ruhY8Iz3VBigH1Ayqt9c00F9u57jGqrRkgBd0ADzFW677S//uXe6JWzMV47CV7TJAA/uR0zgK1e65FV2I1oFBmNXLWAQEcOKlP4rSA1rk4y2gi7y2IJLIb9IboomSB8w/k7q7i6DUSuX7NCGLpNt+nLJP5zoTtftGvDxLiVXypzELKdYQ5s3XqJ8DsRlQM2BnNYFRfVsl/fpoqvAMoVNWuiv5Z4Sm2mVi8W9oSGy0p3xfa2BGpALMOjyGynNVRbzGCbDs0HryROqLxEMBx0YtgzDIurk8cnGM0LnGhOkr6zmf0GvkxuipCeDbaMGcL1oBoZMnfd+J34EHIEkHDiYlyg9basmgETPkp9oRtQn9OBtic8UZmYcKBwBL4s1s/sugVBCb9k/cyUVY8PUi1Mfld3Y1xmYub0ozh/qB9VAUiIBJJbc4SuE6YytRlhD4tb9FFCqUADMBjOS7gqrPTgnnzklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw+x1olFr+T/SNNAY+6xlsbb3HAc5jNQUfPi2EzjciwJqkati98bg7bw0CCEJrkhy/TgNv7MCMyLO+nBimjO9GAQ==" } ] } diff --git a/ironfish/src/wallet/scanner/__fixtures__/noteDecryptor.test.ts.fixture b/ironfish/src/wallet/scanner/__fixtures__/noteDecryptor.test.ts.fixture new file mode 100644 index 0000000000..ad8c84fd9e --- /dev/null +++ b/ironfish/src/wallet/scanner/__fixtures__/noteDecryptor.test.ts.fixture @@ -0,0 +1,372 @@ +{ + "BackgroundNoteDecryptor decrypts notes belonging to different accounts": [ + { + "value": { + "version": 4, + "id": "7c2f8d96-d106-4a28-881d-0575c83fc525", + "name": "a", + "spendingKey": "f66c365bf79c2dd79c4020d5bc1035a11801381ac4629cffdfa005416249d5fd", + "viewKey": "486d222400ee1d48bd41e283c35d2bd40db403d5b0672b5172d4cfd96ac9aa69a48c884a14fdc0f9028bb54997a9e310bc92af843f6d62619ad9845347825b4c", + "incomingViewKey": "f7ce154974e2e67d0f0527f1654849ba381db5114607729ee7a607b9e0dbe500", + "outgoingViewKey": "d572ef776cff8911609bbf1d17b0f99cf7d7b7106ca22e00fa23f4a356598f2a", + "publicAddress": "45e654e5369ac6542c7b58f0f75cd33c438b0e2c3e37a7a4d49fa240b9b30670", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "8047e668c477f7dce2814d832cf6b7f990db8951cc62a56693969fade5054900" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "value": { + "version": 4, + "id": "4b4c7ca5-fd2b-4206-a961-308db8ce37e5", + "name": "b", + "spendingKey": "0b4d99be40fc3749e078258919ac9ad72034a726a200013a07100ed8a286125c", + "viewKey": "82cd94e815cb70ce2a4b6662ce8a08dfbfd161f95848edb7e7f52774c99809996ef81b670e345dae038a8ff1bbf9c49b5747dc5a590bf9aeae6551b712e2bc55", + "incomingViewKey": "466ba7009f92c950750d20749d905fece3006fa34e25add7884db8de417b2102", + "outgoingViewKey": "6e620f74dd727ea99416dcd5d72ab047970b142d80ad68d5f0a17511ad9f3b08", + "publicAddress": "ea4d3578d46302ab6445616cd13a74bcf7de9fb20019ebb5a0b0e2312558b114", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "add170181082155557e9ff7286b63b63d5217924574dad4a8f4765a550035f08" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:zfcr+TmDxhEwAhBQ/zi4z0MNzxv242jhJ4ucXIlPFzI=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:SUuYRMd5Qz0Ciet3ll6faNPap5vv9b0psomXeZokN70=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1719013983104, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAQAq8G7wyJyJ0lbGbSN0FjqU50rhwY8Ryio8RZEi010+k8gLmU0Ql9OoeLqakhwhV/orlG+D9yYPAf/o1y8oslnckAdP24c5kEt1gJXACPZmFHEn77TFYhRkY/cA+zOOSp7XCMjvJZVKFv/xfxQlWmv2D+Sz00+GErhwXga1/WXMFjqm2Ejhs7RneoIeo3VmnBEPiiOKYK8g5ZpnEqDrhonxbyPTUpLzVOSf1FG8hdWCxk+TAv6qyWVqwae+5te3fjdRrSDosdGakBVQhHnRJvJFozLGMb2nZZbrgJX4XEV08w7rBG2D+gKG+6PnwejPr8JjJzI8tcgbE8f7crO0AnR1eWXyrt/t6XC/72YTz7x9uA233fNnVfCJa5gmIx0oYtgTlRGya1cT3dVYfHr6/JadS2IUh/vPfOKifhN8Lb+QbmFiXU0T1Iut1UygpTgYlbTk6Gl3FVEb9gsUMPyBiLDVto5f4wUa17QGKnYreZ5OodaL060Hwiu1eqm9d1NAOFSoSS1rkqQinRuV2pb2hx+lS/tzC13viR0Mn4hfxI0ixrYkm2PCu9vZCjsjOlx9194GdRcr09QdWDcvE5sGTNUrdrGh3Uh1UpqlgHGl2y+/8xlk+8ES1ZUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwY8CLN3dtklbk+71Z3ZBasuh+e7H3gNrrY3feBdbEf4qepCuIcTsr96KrImOmoncBy1xLZ6OE+v3p17MqTbSgDQ==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "9AEF6C533357CADE4EBA52F9BDCDB30438040F1A45C016790431844C500B3922", + "noteCommitment": { + "type": "Buffer", + "data": "base64:6LzdpRPQw5UEiyYLfq+gNCYUUmtuXvFxHYjnYpTXXlI=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:bu5PcDPnz0TRVNVxZgu5SL4dzMMqPxK4gNc7zSt38Jw=" + }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1719013983570, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAZNxfcOB5YKDBOvla+GNPw2DLu3Gm0ACWELT7kUEgchizubqY95Wi84gd/5GDThRPc2cJOxb2I78dnj8h08UZWoDHpOAoTbBCdrrjjVnP1GKgI9OIAa3laTQBiubuHQGL5+B5saW2+r+mS8fZ9+k8trMHhOoaGEYe9CTVrzOW2zwFYCo6CSffGx87oWy/dcnLZbTb7LpoJVoZt2J1Aaw8rhe50WiNCs+zXeKQlhR51UK2QeV89b1fL7m6tlDa9k6/57jdOab8R6TI7W3ytKnvBTDeh4IShPCV7W2FNgmfyWVl9NKobJkqKbT6I7isH8ISJMrzAzMAxhjD+VhexiZ6RD9DLxKYBZ6Npt1CHtb9n4H7qiSrk7x0E8KXWOmwayg3wtCVbdYJROxjtvD8rBsuQ19l4FHWwB6CvC6Ym9APDa6WLL24nnYJMWKGu25QM+uFccS8LneKtZd/TtyashnCkEq9O1m0G0QwklU9EtrDj3ER3kYCiyq5mcgKVEyi6Mukyg8iH+nPJfkLb+yP/31OQKmGF1GoK0zGHvBWKPT2kiprBs73Zvc0Z17o3q+JdrHUjKgZlTr7964+13ZLxpe5+KMABIiazK0to5p8z4KO/v02t07aS/eXh0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwllbQhfxkZEA21dRZH0vXYKiLYIthVFgfMkCZHguQ6bGcJIphOvNXP6I+MPLwCTS6QVfF1gJrVOObmQYO23cTAA==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "1B5F9F573B52E43AD92585AF9DD1A874E8937B7122ABAC70ACE36CBA06B25331", + "noteCommitment": { + "type": "Buffer", + "data": "base64:ZUFgR526J0OKlmRQS/CAroqhfGg2G1xtWGsHuUea6hc=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:GepXK1fb8/C9smsskZsjKNoioqiTjx8C9Qz4AuIdwd4=" + }, + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", + "randomness": "0", + "timestamp": 1719013984028, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAKJusIm4uyCbXLeFzOHmM24RcR6HrFoTl0GeA4jb4xXOvfFkpf7q4oxpJFQPEAu7QjlIzKUGAUM4sMGtGjmcIPKNlu5HhpiXxgNTUijiFwXWkRQ5aTM0l+lsA2N+5QCn/hutfipLICJCPAeVOME8uqZlQGRB0JtjZRuEVDXJcYQcSLf76Z+e5Kezt90MeDC4Bx8Xq7xtY+nSl9y7GmCYV3yctcYZkGPyCl0c43arBFxCQRvYKXM0VAtoifDLu53a5q54etzSu18AhPrs/3U+l9YzlvUysOv575Tvt0MEHdxl5p9AdjWNF7+DqGzhspj8A9TdCG0lX92+o9fZi4BEK73Jg11sln+bGL55rU7DLG0KQ52gbNzwBp3MIlkxb0IFYjUMmdomQnhHFX3ggdTXNTyqiT9+v9FRkdiaZXQ8Lgq3lb/J+8YK7u9GtBYlhWI1JWt9g3gwaY211NAmnfdSeE2GY7fK28h0yRaPGQHKDIcQAO1eJb7RE5x4q8siTER4Qt1MqhUNLNMAMQ2Zw5XwTuRKsxMSmKa7MmCUfICir8LxYf6fV8aRux1Ky1Ql3+Dx8cXjoYxtcoQDOjjlHnX5ppbqcjfQHqX/3K+C4TFDFNYmmKJBusrFct0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwRXXta8pMisoYkCOUozZOpBRR7/g1hSlJ9ld8HQD0Wik58roilhIQ2RetZLgctMVp+4mNV1jCksj2cEdz60kIBA==" + } + ] + }, + { + "header": { + "sequence": 5, + "previousBlockHash": "7C790FAF96B3CBE01175DAFF6701F00A05FCC8A5895848244D2B306095FC62E2", + "noteCommitment": { + "type": "Buffer", + "data": "base64:zEuM9e2y5xNNUEEQYz3H/ojBpYY5fYUx86aqwOITUis=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:jds30z/+5CO/wYdxVkmfC8ojAAUdiIG8cg8J1bUUdFc=" + }, + "target": "9201866281654531936596795386791503876274441021197252859723586932895305", + "randomness": "0", + "timestamp": 1719013984487, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 7, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAo31EbF9w6iJEtStNy5UF5HeE3hCdr4rQs8juSHbFlVeGBStzUm9uvdQswl2VbiuEsikkAmSYpJ2lVAALfSiZXvdZiWCXTpcBUYsG8HRUzamxoLklvfsgHwSy6+jR4z268ViDl2NnxuqGWNlbcSQWqzCPmm7UNCWn5ocDeGAW0wQYJGKt337ygAdkUxHPXgRszAOAYVARhSXcWr4shiik+ccDHJkvQbUy0sIuRySQmSWs8/hBbekusUCQmriSJSds2d/juoZ8uZpF4EbcX4Xhm6KpmRJE6AdwgHKg/WRhneTTMIcY6Ms1dDETHS5f4HES2q68waF5shIiSdkM6DR+W+q/NnS4FhKok0fQs1/ckKz5BpbQLh3TgkchA92IHd4eAyjr+8Rcw8XmuPZP9UBMIznhjPwgHOVre3QXdud1Vgl0b5vP+YFOxQa9SlyYR6FT0H3BxR4rYR3U0ELCG2F0CrREqMJR1vRvrjgoS8yIxWZlxeCnFI8QsyGQnZKgtqq8WpeJ2FaV/Pz2/XugVsGrp8Gv+QFSCuhLIHYC+p42Rau9POk+7V37oEljyK6ORGA6DvyuyqyD2rhu9gl28vuo/e/Wkge1UVDsVpWPck02op1jPQF+zCn7mklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwXusFrGBzhSSnLHx/FMWedk17JjjT+ZH9tbyXQqW9bSKYuQwkJyJg9z8VH5iwPw0D1PfyMK1LsdvKYqO8RZM2DA==" + } + ] + }, + { + "header": { + "sequence": 6, + "previousBlockHash": "C673C584B178FC9B3A7BDE769B4EEF7A51EA9D929C1ED2EA5A4B44249DEC9648", + "noteCommitment": { + "type": "Buffer", + "data": "base64:vt5teZZqKbnfAHGKZbeOyhQQehfMlh/aufipw5zLYmg=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:E4KPQTuECSzAvG4GUVp8+1Q8r2WAnS7Xq7inHoS2RZ4=" + }, + "target": "9174987784651351638043000274530578397566067964335270621952759689537226", + "randomness": "0", + "timestamp": 1719013984938, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 8, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAKpkPS4Hj9c9ixRHHxEjRXdXiV7A+hx1XUOSmK8cJFiuoO4nrqdvev+AVFvpcR4pjQOhgQ0IYvsFfha1XjblrmYOYuCM/Wweyjr/n0TyWQPSHurdlFtvYemF/kATVwwheRxAsDfUN73CI4A28dGvxdiEk0um4acmweQiXPoN1Va4ZiEIYn3a+58vNNlI521SNiJVGj3WEnNIb2pTu7ok8sqQFgICIWyvqmoWI2e6Hl5qpl935x5CZ3JPoyPNvfn22Lt42yqWMm4OZshGr60hyJmaS2pRvO1J+aEfyWelKreeoyATKi+mYtHMibVHWI5YtjMm1hQVI12xflZr5XoIJ8xx9y9QRdfSLRnETJ/w7ulQUshB0JeQQ+SvZK+x4dFcXgksWXlw6convFzyLzefr1zDCF7jcozFRI4WZd/hxKYQiuliHl6cM/Ds03nxkRcHZKcsSmhs2p0Bczx7tA2CvQtivwjzAYTH6ZJMSmuwYikDGStmWwYDFOlr62Mv+ldtno/4sPYbfpeOuiXQ7DD/mW/kxEqmxvEz9OnZJ54/3VjVJsc62oufIuZlZnRCk9qfx046wMPERKnzQNlKSsyy+nOajFwqAIvXTn+nBCsank84d0Z79tlVViElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZopsGf7sdqk8u/NJ9MPZ7YFqPPXX9jlaDJw5qtltuW9m74JVkY4do391FYpEnLVUutxvAX76GnU2fOe86/i9Cw==" + } + ] + }, + { + "header": { + "sequence": 7, + "previousBlockHash": "952A76040CB63CFF045161F69659B67A637045CE326B9126783AE9420A126B84", + "noteCommitment": { + "type": "Buffer", + "data": "base64:XowEcJ2nvlme3vb9TCWJOp2hG5JgYmzeZcMtX7xttSw=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:U15nmA2QKt/vY0DpOmNu62eilnZgJ7afFAg/iZoPMfc=" + }, + "target": "9148187795366513087508709149025146424715856256637674150531751753357577", + "randomness": "0", + "timestamp": 1719013985385, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 9, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAVaAhcF+a4ploA1H9QP92QhV4lCYh5PrmxunwZHE9ooKLpaY6axzOBPyVdeLg5BT3jbr0cOzHnoqHefoaPo0IkhPmhJZzQIbM164/Ndthqb6ZZTewyWeOu/LWIqY/d3E6iE0htcn2goPo890hX1tZzBFiDTv9AJ9/tPNCfu3CVgEAgjzafD2J+QCSqhqG0LpknJe7iwuEuwY2DlN/MnDJH9JLjMhl1/3BIlFvH9YYpNayEdNHeQ5+bRaEt+3xliDVDlLj6qSwnkai1hyN7NjFZQCgW9PALi+WldskVESIROFQXKxTZoiC3Lg2mv/c1/c0JxZO2s3fsBKC5EgIVOXMJpQB5ZSmenngk1atop7hWaPW5h4ENmlD8YtQevoridU8dMimhPfxr85uCoGhaHqxAtDPCh2rUMUgMIU/1o0Cokz1W8v0m0wb94pK3DZyW01qbSiX/wpaE5O+mi3t2VH789JvRXVDfMNPp45C5VMgnyKvTdwG1koaGzUqu8qRd8Db9b372iHXBaNHJTO7BXZr9jhoY+ZMVSFKQ6FAUAu2GHARBNqiTPSghk8cD+CcUFVVFnGUsEIMLA76i2G6Hw736DPs/Od32tk6tLLy3L/ZIp3I8htZtzZqOklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwmjrNuynol5WyiDDUhbMWOzJ6t5Ege0Rv6RTMllKRQiqENrQkAEcrRb9LF+CgR6IlngJ8OTnNZxkSTZZD/90DAA==" + } + ] + }, + { + "header": { + "sequence": 8, + "previousBlockHash": "B3DEDD5D8C85C37BCCFDF449114E6DE67397003A405BBFEB50BD2BD21E8B4B27", + "noteCommitment": { + "type": "Buffer", + "data": "base64:zSBTZjhUjSW2wQS7w/yFg3BuUj8igHc8REsCXYCRgU0=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:mkiBdnLtYJN2lug0zNW245DPmmAFKExG53giOOEQYmU=" + }, + "target": "9121466311864876128923245652724724632104869735746188813030060672759072", + "randomness": "0", + "timestamp": 1719013985850, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 10, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAe/iqmEOwcKZ1RHevgjbadRohwDQqN77ozsyQeFJot6aKqrIQqvh1iGQpkNYExElCm+TjedkNHGyKhV1FuBRrnZanfh6dTT+NpR9NyA5Cw8uFUsCVqyndQi7b2Z+aRcqDZaCKofLSWnW7Ip0IfTaZgp78v2HtznDB0kFpt1QWe0cCJfLnzvBDY1EEi6/JEKgRHTVw+GLA4n0AnC2pUq25LlCrTw8OZCjyH9jV+7tfFWqsQuakW5YxRh8xmfwc8X/db92kxlV1+APBd2k5NnNoEx6qmhZeCRVBV/fvyGOUKQDjJXTeZuLrZoYiBDdn4QUq0oP3wZq4P3vhwf5xQNRQidtWCCEZDkunygFu5qBd/9L6LEw6XJA3giPElz2oj+ZzGv2N1a7WLJaElLZ4SHuseHTYPJmC/znlf0EJXIUAr1ZqaRnSIx3NTWaqlelaMnJKb3QsO91txLPEKlY84pEjTqBL/OUiRvqJiAHhy9k3OuSo+0CpVlIILYSCod72ddgL1cKwTQo/Emr++UBp3l9CoR0lOym4QqOwZxUdESOBVfvYjvkpSsIVwCQ+cRmTZ80N3CSqDczEq6L/h72yMRkNdnDzz4kAwsOscJg4MRKRh5R4YXx2zEY57klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw+BhqUywJBzVApbpJNc+CNoUVtT1tJKDOaoFHdPYAHlHOCWpUcfYWeXY1Yc3Q9D13o33FsXhMLKBMRXZsovB8Bg==" + } + ] + }, + { + "header": { + "sequence": 9, + "previousBlockHash": "6832C7361633BF97D10F3FFAA6BD1C8634BB33274C5B89938B487A1433E19863", + "noteCommitment": { + "type": "Buffer", + "data": "base64:XgO5QrXMKhjdE2HvXo4aFzLmlB3S8jZ45l9ok890mC8=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:XpOB9/4QyyJp5Z2xm9HEvBdd2z9tdEsWm/iBS6dUL8M=" + }, + "target": "9094823328238119324660168503613036415495463326164889575918026009508991", + "randomness": "0", + "timestamp": 1719013986328, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 11, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAnYD2TvWi5cr0GeFdDSkpF1ySC28asDLBqJQNURctoDSHd7eaNiMSAk8VMyonnGnc3gv9/VZP60Uz9LPrGwrg2tOJgbXF5lVy02IFwnhccAmsCChQ46AiZAwSu2BQ/BUgs8zptmZmv0VByJfT6Lnmq5J03GUlbBYpzUTwR65Zo3EITPHA4nH7mvZXcCskqMx3WR5L4U173iYz/bfFeEw3h322NyvqFqp0Du3otzILyY6CD+X52ZRsUSBHKoaF+F8/fmgBwPR0IE7EduabjqlC6Mdm+2Cmmih+QopyHIsry32Ey+QjGQyyWz9M2zWrL85GzWxRQRLzjWZDjtqM9IWZHp8o3R/FU0SXJS90XrXedgMFAk4L6Ixu413Sskk3FoYfG52pBmJqbVVxREt8p5dMFQEjr2/qW6fkZmaqHQYuoJ/vPmMItlXtJSSNDkjNnxeGZa9ok2CzSZ4M+RYIBhPRWQk+NFo8AjDZyYyAIjlC72QujwPSC4++dmDXZF/1PwG9eCctK/nqdeLZINXxE9rDTKv4CgZHwe2Zkqr6+2omPgtzuVnJWlBJybJC/WjyxbR8n2/QHlpTU7j5qvEE96ZvXqUokjvCLmofV6qW5nlAw90vYS3hJQga4Ulyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwLFsK5tlrIkpat7cVN46dfaL70TluCu639PCk5yYotx/LRPD1kZOfRd4Gz+VU/bHEbmwL/Jq6BEWaOIkGkSGsCQ==" + } + ] + }, + { + "header": { + "sequence": 10, + "previousBlockHash": "58A0523684A584E001C874EFADC62F1E460EDF2697B8D9F511E18642E5F725D4", + "noteCommitment": { + "type": "Buffer", + "data": "base64:i4N5y96CuXjhqSFostekgl7/bhDtPAJFZ6HmO1++yys=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:bWKBnZxozSWU/k9Z4+Smbev7L8FCAoceXwrF/MRbeNY=" + }, + "target": "9068258834662928698220540790897658244352076778286486653826470223999191", + "randomness": "0", + "timestamp": 1719013986791, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 12, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAxBnmx8YmVy4Uba/bf0FZsUaXaevReHvPPTwzEcnAEUKvNBqOJTopDOa3ge/F8fij6EGE7yoFexmwpFirko1WH2NHgCSPGO8PrLwcg35BNe+WyBCvo8wdJrGgGOFfvEsRl/iTGYlqXEFeUpj4j1suW2zEFb/umx0UHBSDdkHtaGIZyTC+G17B5hnAbfBtNpG+Wkww23OB8LEZjdCN8ALT/sKBnJDI7Q6iInf0qH0aQh2wngF8LgL7b5DziBnE9l7sVclXo+SQQxixNn+P4CUS98SYVM/HFaUT6CPpsJeJu/j/pqT6ekkbDhdmss934GvP94wQLa0eVHxlwAl6pkrxiXBbtR5G98Sr4z9rXX9c3eIl0CoBN8Icoovbziuj9whuwvhyGbl2Vgb5v/3JCSO2xuglGtiRGdqstZlJnafN410nX8dmOtDZ4uKYbX+91ElnO2H/zQk4BnHCIuKYWO0pnlw0qrQID7s46ajoubh246fTaRrN/1ZUhdCzWEnkMmFy5tqjG7NbzEiJrK2S12ap5WsSi+QaHxCILZnmRIR1UP2lv/iAB2dtS0N/t2aMV6Vx3V2WF8VzLknEeve6jkYaOONf8Vq8d/yHjGKZbqBrRThD3hsMGTH5eklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwVlgb3VAxYA4/quGjTO2DnRUhwBax0oH45Alvd5CHvUUKdFvrCkSP4PoUr0ARmkKwZ87ZCdwDRDmmdp0hVL8WDA==" + } + ] + }, + { + "header": { + "sequence": 11, + "previousBlockHash": "EE1E11960354D5C94848038886BE22CBF0660BACD52A44E69A8C94ECAD783D61", + "noteCommitment": { + "type": "Buffer", + "data": "base64:DqLRDXMZ92KgoBoHoEEvBul0Qa9NKA1GIxOCZuwWqnM=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:PRosVo1ZTB77kalVWa9c15z/6GsYCKd97N4IJU2sXPQ=" + }, + "target": "9041772817458669358631436925553476123971485443441062513642264290171806", + "randomness": "0", + "timestamp": 1719013987272, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 13, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA8v2KH1eaCGWjnmjsTf8l4CS4qrP7QtikWedpfGFIjZeTY0pFCd4LUpChinhP9zHbbIvKxHf4EFZfra1pG6lWj0ChaoJnExsHC6OwUH3v2yO58kxa4muyzv0+RvUM2+ufyLHNWRcD5DIoBSTaORxGR39LiFO/3d8V776E2EhxbScJXyBY7kN+RP57eoCvfiBVaB0F92NZS/oQAdwJGqJBQpfMOQkXycx1jnd5aB5hsByJ3Pe61FucFx7hE26aLknQSqGhozj9Wlb573FIcJZ5qV1s9JcIZCnoVMw1YZOTnGndFvgPcBbXCkeQ61DMnrRf2/OpjLluEdzKB+z9L/q7WfPYAR5oIHUYKEOGHIPPBlc5WIu9Uvhm6W18XqWjmdkgETEmCjnvzYviapfgW1/eQRqIc9llgIMvgFPQG9hueZMyoZdaZVKlUgsOWfgxNbfGKdXbNTqPQdd0w7nV1ddPwqn0ct+eEtbTnF9JuoQNTQGys7SvUx3B6FiN6GPXCk3TuqIAk1WqY31A8ZAGXgw2IjYVWVaxfbVbsSvoox0OhnqwIKKv9ARshpF8jOk98MaGbARGRDu1G08d/38CoLblGp0rc0xUt0GpMOamD2bVWYvCvNlnAXiU6klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwoaqz/TkttWazh30GCjZi05aD0ExFz97kO6rq+RU6+ZnULmanCEfTiIk5yX+RaXVX1FCz6ygL/iCCkJFzaeE5Ag==" + } + ] + }, + { + "header": { + "sequence": 12, + "previousBlockHash": "CB4DB234446AEFBEBC3111BF2861682D233AEBB9899A0776D1B230CDDB7AAD50", + "noteCommitment": { + "type": "Buffer", + "data": "base64:MXyx0nQL1BKvC52C7mi+es1dDeReLAAp0hidjFU0ayw=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:lbUUxQRyI685D5d8NCxSs77BBV288Vls/6szjg4lwqo=" + }, + "target": "9015361047625083866771187507615534750461425295595622380322060663659456", + "randomness": "0", + "timestamp": 1719013987740, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 14, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAPrfs2jjx/bvllkzfBQxXgFv6RZv8Fa7mN2koSRMrgTCZq8oXgOrPCvT8NpuIfZ5lM+Ui4HqR00HWa84Z/BleqS0Q5jdT+vIw5ne5lNqUc4SXSkh8u+kXnXtEm2werA1xGlz6Mg31m1T/0id3DK1PhqVLMyyt1/QLtEpLxAzoOCEY8LswOKvdlBePclwVuHm9miQqDTrFRumOXVjvz75eJPQIF2WNugKXcPqhtHHKeuKByHPrtnWoVDYbP9qlS2PHJF7skRWTxkh0nwiVjZXLVlPHe3vrs3npIF7IMXb6UkAxLKcEoLjSf8gWAuJ9kz80sC9456UlL8LddQlafDhqoVO8SD2v22iUqhStGUOqqfuav9jYpPTJtKTsxyMbvyEe8gJJtoJeDyJZl5PWW4IKuUfU0JQuJqw0/WW5VzWPeRSsBv5uVOasQGX43r99wkX55APEa+uu13jXJFrNoRVMguMssjzLuS0rNthoFM7yaLJ7O43ruRCJ3fFehJ98bCUb/fnRG8YjMTac0mNt1oUYqyENk+u+qFOYqrzRAYZI5zip4cYa6gWYd2tFrs4Xhk/ZK72cnTp9ygyyeHPtKxPw8NezsIkcrOtsGIBTY6OVR9X0rF/ThNr+9klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwLGUCF/Z+XiDH2AgVuK+HpcZsvlpJ31H6ecDPjnNG90ECqN30w/xRMC/p4chq0bM1ZCq+zKySY0ns4HQV4jt2Bg==" + } + ] + }, + { + "header": { + "sequence": 13, + "previousBlockHash": "985678547DEFBDB3E3875327CA873EE69A3616E9DEEA2893F4E617A0469FFD4C", + "noteCommitment": { + "type": "Buffer", + "data": "base64:4+CqnqYonOwdj1ImVbCCN1MzdSbtT3rpmmuqhMkPzjA=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:t9Dh0IBHhXIjkc8pS9gbvggzcy1tplXV0UsKJsjSvfE=" + }, + "target": "8989027764587843972078000359639078132662736945816568766992021111212360", + "randomness": "0", + "timestamp": 1719013988207, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 15, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAm/YVZ+H4eL6lQXt1cRWkNAAJ97Xo8fX55KT0+/Z0JJuR6MMRsvCgk1P0L7DfVCbHlFqSHyeylSXnVgyc/3YGlkM1lxohBzHA833kIWzeVHq4K0KYoTmHsD1zLaMY4qlc1BZSwSgXJwzwmijlM0+aHCSXwfIaIzQMvKYwB9KFPcgERtWjSaAzFm/2xLyGB0k97EeGjgCj4GVXIHeNTL48eMJbKkqQdNFdZWfh8ZxoVeqzcB5et7A07p0HUPuJcXfqqqW5/HhucZp7++27dKT2V+UHriHNSf9jkkpBEGs6r2gMkSFw9RSoF/NM7hRCEsPBceUOcmxGDwia9oMVXK6IS5RhomnxrmJf+doryU0xsKXq3Y76pQTMSawwT9LzoG9LMKJSZPPfx8fzXUYQDLwFHUFljA0dxoFXyxWFk4M5WjK5hDFcjVRzqKnJEPnEGBpxuiVNwdWcq5kuUCf3A21L/SD3nzbqLIK6aNi0RaZNV4rVHk7CIIzNYtho4OcgPtjA3dE4ltM0RUfhNC+AHIRGQbjgFHlV/kKc5AGEf1c+I804dP/L2EjLOPUq9J7eM4DWt2GhhfDRIH6EBdM3Z2bgT/nB0oGKuVHCSXM6dxV7jsfZZ1nrvQlC/Elyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw84uTMTAkYg86x/oEy8JUbwEE+zFrZUJ6PutlyD78PLEdNlgTbfLTyilb/z/pseY5W4Te5EJPPHwaj6ezR1agBg==" + } + ] + } + ] +} \ No newline at end of file diff --git a/ironfish/src/wallet/scanner/__fixtures__/walletScanner.test.ts.fixture b/ironfish/src/wallet/scanner/__fixtures__/walletScanner.test.ts.fixture new file mode 100644 index 0000000000..e4e86e3d9a --- /dev/null +++ b/ironfish/src/wallet/scanner/__fixtures__/walletScanner.test.ts.fixture @@ -0,0 +1,1764 @@ +{ + "WalletScanner adds transactions to the wallet db with decrypted notes": [ + { + "value": { + "version": 4, + "id": "d1cf4a73-26ba-463e-84dd-db70931caaef", + "name": "a", + "spendingKey": "dec96500a1803c548afc31244b1c3c2af1dd2e656a6f5cce4eae23b40b8f245a", + "viewKey": "c2b12faf677f79b93f770adb4ffc7c2e3cba20a277645a676a58724782ffe861d1d88fcd45ed80e5e16a1320ff875250eb0f614a34d368db9b7f2b59efa5f93b", + "incomingViewKey": "d2d98c9c0cfb2051ea4dea5948bdfa35a18db6b244706a48fec8fd9c0eab4e05", + "outgoingViewKey": "ebc96689717ae1fb810465e5b827b03c849b4e17c9caa336190159db3c212b05", + "publicAddress": "cf2deda03dfb466f832c25aee99f6aae5e65d87ccf89120c2168eb9a15e39000", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "d8e8073254aae15fe24704d590fa37ca0c396026ef5bdb2c6012bf044d5d5408" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:itEBRkuh+NpQRbD6hs9/4O9Vv7mfl2/pQ8ntJJLn7Qw=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:il3l3FuDvwOhXk7L4snmAUPdAMnJZYQ6UotnfP178Fc=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1719441703087, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAWnEuHSSveUq4ZoQoRPTTrnZ/6EFMa5OBfv9Fzk2gsDaRTm15GIdSlbIVvScNfI5gCG/SelzULkElM04+cnrE1j594IpBErw7xWf2mxkNOsKZ48P0tYyyTEPlXp98g/C9EAY/4pPIEC+eOuCzboW6SvPJMzecOgYBuDhpMhaG//oRDuNHvYug7JoFbJH6wObIWhJM6oixok3tsFKlT8Qs1CJd9ZBrtfKwZ6TpEC/0gbGY5Dmy1nmgVG03ge/WzGTRPuYrPHVbxR2qD9PXwrJhKrs9x6aGbwG9uAPKounEJ5wxuEhw5MTscaUknt38YpdhcKLgcAniLt3Jw/QWeKeiCvkOTLSBNf/dSDw79YFEvFKfzi6dRV7v+myr+M297MIRzmcUX11BzPP/2IHji37MC3OWJf7Xrj7qu2OS1qU9+MnPOdKRIb+YCrrTu22xR0ya3dpryD0/5IuHYDFTQAMYqBXDGBK2ZHhsz8TV29T9Li2ggPKSqIN5M3zD3wG5Sw4JLtl2/g4ulpCpskMwGGd+ft2Q0bMTC5rko60WNf34aSJHCSl415N6yJK4vJFQK2e+qGsbrcLTb8UGgIaWIOssGAyH0z89DMthYiP8O80uoq6nvoS8rpLhiUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYRGel8eDPjscFom3Ss+KyCvF2mZ6ITTaAE3CaNd/pQsH7kbKfzN3/4PetOD614d/IyZar3jqwioeDgp2o97VCQ==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "BD771382B6F51DBAD16BBF20828E397304360B8F4C41EA4AAE4F899DCA091348", + "noteCommitment": { + "type": "Buffer", + "data": "base64:GI9hJ67TDZtvgeaDQWMz5v8BU3x4KLOebmx6LX3IDBk=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:apcufvG/5MYo2BuJxXgf/0XKvpyWF1PUUVfMbOT7kB4=" + }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1719441703553, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAev/XVrZBMN/+tj4cnb5nDkkOKHeMS1Oo/udw+KFj8MSjevA2febVCbQWbtdVF622fZilPU6egNmyuBKSRHrqFCjB5dDFINgwyBP5NBqmsYeIrfnb/2K4YkHN6aWl7D+PH/p1n2DsVyyX6LyQtinl7inmIXtUu5G5zHKTJ/3aljoMSaXrtUuvyeZhXiT2vNEZ/aju1zsFZU5WFryRBrPoxdE23WjhNUfz4xHwxPdLBfivYp1O2jdNUlG6INpxNWxxy+s2pH3ct7Eby6eBHrEzDR7npfVjQnpWdMFbFnTQ29aGAxg4fsGM+H6QdCvvUg1QkUwnPfNC2UwnCg8QlhX3y0b/PdkiF/0hDetDv2bMlBWU9fwnfzftnUlX76pA6UFbJp6IdKhRM8Yu+Pbs5JkdO1lXDusx4Czv1kzhWqS9psGjHMboT/pCtLhZaeMmBI1LTaHSgpfq/PhcdQ7I2wzLmnztZQz1eRbf2uKmycLqiWUt0zvqt8ODu74BWzMVD21Yj3BM9CthY32ikWAUzUnI+AAIf1+eeUD/rxcI583dl5cmGhN/fYP2OlwF1rZBk7soFrOT5bvma+w3+RoqhjzJP6lT5UHwrY9vRSd3BipYWvytSdUVcI+X2Ulyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwK+0oMAXwcI6ruO8mM0tz6ivDuImpFkTRswicw6hYfgtKM1uycguZow8m4QYbnrU1xyRBWrqAbeHelR9cJfedBQ==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "C3C3D2CA169A13FEDEECC66178A2A186F405B60CC5B7E66C795464614A752CAF", + "noteCommitment": { + "type": "Buffer", + "data": "base64:zWgjVHbnPw8JQmC9NL5vMtrb9ReTUjqCBvHUS9DBFEU=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:X8E0ZOL8NQZcDOubQBJRvGi7wg4cdEQllw7D1Yivv0I=" + }, + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", + "randomness": "0", + "timestamp": 1719441704027, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAd/4AdJJdsAtp5NFcJ31d5YC26X8zbPWS3V3AFRedjoyG6S0abi+QVAEL9+R7+dgh3rIVUjfLS93XTdRNZI8HS4fHoCcMs3qy/1DGARr6enW0nSZk/AwN8Hdjs68ykYfzTuxQDMJrlhWn6h/XctyymTjlqIHGA6kuRP7X2lB04F0EPOei4YZGCsry42zXXQUmwxJIPEdZk1DCYmoaVxKKv+0ww1L7Hj7Xs5o69Qt95h2gogpZKKaepMbteFB1Rn3Kfz0xSpkx79A61Q6BrtEDfRbEF/Dp9EDzMyrNPfM4nLiW/MnMLReIiOU8JcnkxcGrQPu4r1mfW3ptu1mVA7CIQxHimKAmHmdt2Y9khCpJQKpyKQ46JqIwD/bxCqcfJ7odhYPzBMrNc8AerIs08aN9odE9A+KTskRo0jchZJuEaaf+2NvgRRA9w2qYQ709d5jhf+gIlWxFghhonu4kKvF8qJcZeyQY4wvzhufeYL46bx7w8L/0FdZDLfXY/POIgm6/3//GSAmx5k16Rgsi3BrkT7wv+BaChtxRuKV3sji86HxlpQOW88Dlid+g5dRj2wsJyfH8QjkbcoIrq4VIMqfX7RqtlRFTsVoBCQx4jOhN0R62umnKztgN2Ulyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwWkIKQufSaYEC5DGaLxzHP5Kx7SDL66mZgY9ZyJnhQ1mhH+V1bMV/aoBrG9mVSkmG/sy8vlXN4WU6jk7Eb7qgDA==" + } + ] + } + ], + "WalletScanner updates the account head hash": [ + { + "value": { + "version": 4, + "id": "4845feb1-b76b-4675-a4a0-0386104197b3", + "name": "a", + "spendingKey": "8864a332b9ffe6ac4f9ebcb5756086285e3799f30a432cdd7ffb9d1a6d39b000", + "viewKey": "09d115a480a1d2b1b8a149a68542a7ed251d573af7c807b17e98bb529892d8b1e74605cacb2e2473a9f83ec96d8acd3e435254dd63b6b50bf4cb1f67febc8980", + "incomingViewKey": "958e5c34a309ccd6faad896122a1ea549803d423e78c002a3fcba6cc7db95704", + "outgoingViewKey": "cb7ad68454680fc415010dbb0cea2e18dac420cc34566fda8047bfb4e5d74ba9", + "publicAddress": "012d22c53dd3aa7cc986bd8755f8a7af024c3154bc170c8a588d4dbd55bcc416", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "faa1fc9d7137e3cb50ecd6e02dadfd30af82a1ec5c2ea9a8e482fc20ed60ae08" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:0yP53NgcgapmcTlawnhFEADpOt3D8bPEZTqA2OopGSE=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:4xqaNuh/b3OqHXC5adH+PM9V9G7195kNile9LCOumDc=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1719441704807, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAADkyAZx9VeNdAbJwLTnTKAe8+xo/bb7LVT/oTP/wHjQyGaAdsW7NUXP+Wy+l6oRrvR38y+gqpM4ArMNb5bqAHtUyI66srXud4dvDQL8wEkD65l5HzkTr6ez8bD9EKpvQXluSPu31cn6JsmYVPxzfdi8JAXsAohW2jehBOx2bqMKsHpcp9mDatVtBs7hwtJ9nD1mCOfk+bLJqs+kxscHxtJj+4SsQ37KoV2Wk6O7XL+5CV97iBFhfUtrh/M6GJjSvvdjyaoM/FBPe6/8DmFV+d0iToMW6H3UiJda6CoJ8kKJpYi7Y0lz4v/M0tFI3EiMPrDADlwOGpJYZ0S20TFDemROedpEs7IOkjfHhw+CL4DurUfT31gW8fBXklb6VdbaspTAu/jwoUUXCmQ29l55PHrJz05RJJuEOe61hdbPabisWisiUIk6eeR0ijQCWzUTOFMK0ml+fqdPQlS/Dd1/MlUOpp0ApM5zyrSbHw1EHihWmQu5k/NjScIemsKPYItfqHCCiIlwvE/sCYEB9J4UF385h0Cue1iHIW6hBrSUMREKuTcVB2crFrAaZc6Drx4/foy8b74IjnYFolJnj/5BWj0b+5ty2NG67fUroo3+P5E/sySN2oekhOoElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw8K31x/ZrfevKhpGEccgYy+TZ5zIc2kodOYlG+rY/DEZdgfxlxpUL0dbUine/x0KBZDnqPBOqxadikP+veqDCAQ==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "84E024B89F913F167200B32F0C043FEAC806E0FE96B2B2CC0D49FF9D6ED413F6", + "noteCommitment": { + "type": "Buffer", + "data": "base64:eGT2PrD9wxP0pCoqeqOdHuCEOmoCa8Pp+c6NLtxdmzo=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:RDvoWjRIxsSVM5ctsU417Zwa6H3H9HicqiHES5dGZI8=" + }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1719441705285, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAALOJRFX+2wNDH/UBKelg3Vvh5g5AfkdivpDMA7lMhty+UBHCJXFrlkQRKV/kNlakvAklq6iYHZYplbLlvvAbLvjG23E675POU+CeZR92crOmvaEMUV+SDo+U8fuJzyg0zkwaTi/oUBWs6A7q+5tYOib/mgKHekf3/3MjixeMCGpwOUHeBjJIiNmtGXBQHlSJhLWb3VJ+UvdRNcQAEGDpB2iWfqkgv3HRSehYlhxtvNBGGM4m/y4z05O55DbIBua+fZKWvn7ONZwJix0xyGt2ZWYT4t974cGFmvK2qgwMGf66A1b8D4g0Wlsox/7k2ZeYwLegvXVDoY3aff2pPvKVfuvCGCqSEcgX5mmGpLHSL5SDPrD/Rgv0HYJiPe4sShiEkYjjiynfHdyqz0wII1m2OQt1OZC8Q//smh/Wi9uuEmo9Ukd9Qd2+X4sEB2Pa5H/QCVlXh1N1muRXpH8feELQKYw0UyL0MK8XpeulcFHa9QLUoLS0tVuiJ02of2dqPpYQsoS+EV/Csj1YGqxO+JfjondVhNhwoLyl+4dinOpb+7SIc5dgHaCVFll6MQpH+mkc0figt7Bn8MOXPCq75BwhGvcJsVHNQeDDr3Rhg1lm/yDOcg6h0gHXLoElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZ+aYeJt37LklpZxHcMExSuceAui6llbkPXNt5Nva+PAka8Q6LPw98mcjbodVcxjgmri3iFAKfa3cWehZoNzkAA==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "92C047025D6E0D505BB553F35E15A608FAAC31A096EA5B1B80310CD04FC1AAB6", + "noteCommitment": { + "type": "Buffer", + "data": "base64:4vrfGlMPz/rVKYTJk9cR5DiFvvltdENwkp3iBQWASyo=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:yuz27wPlERZ9zvnUtE9m+9aJGfzQaueZdRXrAjFpd2w=" + }, + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", + "randomness": "0", + "timestamp": 1719441705753, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAVV72G2AbMfeY4iqe9DUR3dNwVBULTM8anEaHxKuOmWKHz0JYmYQ+7Dvg3eyhLvqNzM3WWPCLVmr7EaCoEU1Hb5r97OJzHx46HW+JUGKB1uOWg0Y8IXU8YFZfQTkO4HRMqS9Ga/rTUYOjUKWYNaz98f9fK/cdd7Zt2A+cgxXj35AA0ij4aP3c+CSOCtNGMlXRpwckTO4kWXij4eelqzO957265yJlk5w3IkNwsakjNmioVHFoSnADKvLNmdP9T4OOGkP3FBzGIwsLAXstXm0CdZE6QcJA2r1nfrysym6fi0l84vRCgCKr3UrAsdgjsyB8L7RNY28qM6rGdBe68JWFWR7qGh/aJ82u2e6+OXb7QAgq1O1FtzGvP7PpT0r0+p9tQP30ZC1oRTAxVokTBKL4Zi+sRkIFmC6nhuDoKrhaBGLqlK7TsMdcQ8eSpGrn53HZmBdXPpt/xnrrNwC3YoHPAR4f1RQ1J56aYvJi/75omAs0ePBdaafeXK+MU2rX12Yj4KOARd1RT49zdcm5XUoS+Y3XLXoYEYDfsKhK6Z5RFZfkpo51su7LYAu1N3pJhxpDdpsZ0T1wtK1qQEPr+TonfTKBhFs4iakIftqgR7KraXRTMPCTevaGb0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZzTk55BdcsixSDHZtS9Ucqx38nIr4P+IUUNk5b5y2rLmh3yM1GCuspaNQb5nKQGa8FoTjLa4/w70QmDO3LjRCA==" + } + ] + } + ], + "WalletScanner ignores accounts that have scanning disabled": [ + { + "value": { + "version": 4, + "id": "065b1469-8875-4ee1-bbe7-6d36561d8de1", + "name": "a", + "spendingKey": "fd0a9e9f4d537a70c486eb2da068df45f231b6aeab564cdcdddc4064fd4eb9dc", + "viewKey": "25fb041e50363538fa6f9657e7c8224ff63a1954c55f61c130d1b1157dabc0adad00669610f7247e238f22f0dc359d4844932fbc86d99057d4e82f66534de65f", + "incomingViewKey": "b53fe8b6b6d13b89bf030ef7764855ca4bf8d9f24e1033e942c2038ca1d9d807", + "outgoingViewKey": "210eed54a39f09e7c97ed4114d69d1ee265d0c724da681451dd461ce353d895c", + "publicAddress": "9fced945add6d7beeca3bfc0bc951f3cb758463a3845487b96f2187c086b40c4", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "5ef2b62c3676bdf5da4d6915f0fbca870a1852c605521caff0399e0b9b1e4005" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "value": { + "version": 4, + "id": "c986f231-7f91-452f-a91a-2cf792fb8b5f", + "name": "b", + "spendingKey": "506cc73cf1bef79d4d4d367b47e84c3a13d238cc61dcc558579ccfe453a504b9", + "viewKey": "c1a3951064fba9f691d3273c47c5be0d45fdc4143798a43b918f613e87dd0a0224001643acfa030d51ff991eb925563045444e8183869f1282663ce5adceb384", + "incomingViewKey": "0ce9869ce32bb9b8363ca02519cb37b7121a987005fd3ebc7ad4bb803fc49c02", + "outgoingViewKey": "ae150ab59040f0ac65c451cd68122b37e75aba445673d1dd5fe0d4f77b4f4386", + "publicAddress": "fdc8750b5b06467ca88a2351b01fe1c04c22374cebc4910e167532428d0f4dc6", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "9dbe4ceefa60801569a907266c225bb491b1b9f7fe77c6338faab50395051300" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "value": { + "version": 4, + "id": "3247103c-f0f6-44be-aab9-3caebdeeed97", + "name": "c", + "spendingKey": "0a20db55d442b982b7ec8e472d4de751253656e7a8c9d8a7c4a2bca178fd4a0d", + "viewKey": "e12f8440a0d53301d668fc283f7a8a8ecde21d3c0c61fa219416bc17e48d8535ffc49c82da7f38b40bbdf08623075f097227e9c7ce136b95bc3465501c5a395e", + "incomingViewKey": "9d17222d43f5d3810d5c8c315ec56981d62bd449949e97ff15084f683f442d00", + "outgoingViewKey": "61604829e709df68364562002bce307167489162682dd783612dedbf467b2d7f", + "publicAddress": "7657b2be336b7f421204d3f77d6ef6987987c0ff26d2eb126d16df76377b6102", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "065b39b91094875391065c11b4537a87c56354a4ac5d328cc030a870dde6060a" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "value": { + "version": 4, + "id": "14622193-0462-478e-a64b-1052fe31b17e", + "name": "d", + "spendingKey": "9a5484f82aeb1c23964d85390b1c4e003e4c640652d1617638454ac0cd5ea1a2", + "viewKey": "c6ee78b3c8ab7d29a5946163254036ef01ee62d6579bcbd7a4877a5910f6bec6ad767a68541e702565c185d83b79b5a34bb546dd0f2051f2833bb865e8a1fd9f", + "incomingViewKey": "9204a43c2e699d4d1c1ee89a37e8125c11de657653462a1130e76e6e34983800", + "outgoingViewKey": "ab6bf9dd3118cfd00564519429a1d4a81819bcd5a7b7f7cc82317c72ba610da4", + "publicAddress": "58d0e4790b30940363a48dc576826307f71ccbb8258d79fc47f20337269ebf50", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "7909244afc59a8a93f67ed1e6dfb5a424d981aea2aaeaca2c97e25cae6403a0e" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:6jaJJl61oR/k59FxFDFByNwVYS+CwucrEoluoYlFXGQ=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:6sKQ4yRZDjKOT5nvXwWg5LZniN+7sfyD0AikFfmgqxY=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1719441706482, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAqMbKPDNR2Yts4XeNiR3HUxI3t09nM/0J9tUHZ5hCFA6xy1hGlqQJqCnwU8ZWqhdxRQmFio+Z8zkts/vig1T1QXcot3t4tGfzlNt+yUxOORGgrCjxHDMI2dWLRlE+SJWX/OvjoA4gjmcXwXXxRPaPVXGJ20itMr7qNQtZb07GSnEMyE5OjS4CetFJyvbyBw6/eEta4ygjzFeqKcU2Iv5QoOoSE405rF/ZdUsajTjF0GKCG0jtR9wAX4dUKnjTDa0ZLh56cWQ2DZQdJ/oRuRRC1FRpi+btePyLfTnN8XjqwMULyQJybCMhGuCM9OTyhZDgohAAJbHMhe9RGQYRxPdwsx5fPwV+nLoW6KeQhgAcsGTo3bp3+Q8PtVVbVs2T4GhsjbUk5oco0ZNK5+BW8ljdEgPfXBmOb1PPv9NOeHFzRj/aK0ViaBhYC5Iuy4oGyHMXscxwVdhPdKpd5+7rGHeAXIkF+Sp7glkvhBj5yhJk1xqDZYZsLmRYbor8/n+SVMNqxO/5Px1QIOriMA5yP04KdhBvgeNEqWi6ivYRfUnwvOKTOKA693DkoRJ/A36fSk+GKDlm4X+94yUpvyGYr9XSGyW7N9AFi16wBqBWGQqMP/q78l75fojYUElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw1z+InxdLdAscu0OPEqQwCDIahWk+CCvOgRcfuc+JwuFM8ctzXOkXW/Znf/cDYKz0ONx/fDUNYnLeX/pI0P8QAQ==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "01FBE0D832AF2D36749432271E8E02FFEB76F377410E73C66C59BAABE4274D37", + "noteCommitment": { + "type": "Buffer", + "data": "base64:wyhr9qpeBSVCzDqyyxvbDVG1+8LQ5Q6pbIVtuHrOJFI=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:/RoQcHHeRE1iEnYSvmRc1tA90LB6kQahXFECIx2uy7c=" + }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1719441706961, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAKsDfnw1z8D5WxMUkzL1jM2oa/32D+JvMnwvl7GiYA26XfVg8AC+TYl8TC1SYiGJaY74jDGrVp9szDWKTraM0CIifnJI1JLqewZipjMm/C2yJzyZiAH0cqMmCkO2uDkl3eO/jiHnO+QksXcQE2ZWlsu9/OhQlD0m2snk65d7mmNYHgOXpsASBTc9HqTyFsNdjIfmRnro9pc+5GGw95EuTiNHzoEzDQs5sbzOfVeEFo6WrL92hiXQ6Pz+nZpOtUmT3KorezFva0Nm4pa1GOipFMAc2zOWI1LNSxQuAd4r1RUN86/o8kZxugBsBUF8edT4uQyWvkj5Nm3TFOPlb+Ek/m53nDCDh4mBqZdEZfFYl0uxmQ1BViA6SfNzW2WPf9FwEikZYpTUEE9x0yRnOcF4w5L/vXyKRIUrs9mU60poJW4ZZ3kGOIofurPE9O/p9yRDfoqkoNpJKBI+Yb2EGW+KWk5JG7KNyufZ1rSWckFhFtWHd/DoFHayM/Dc5iH/z7GnZyVU8R4Cz9IAR/v8MEjSkc+M3+By9INB5pOK36jNzBg7RBvbQX2CilHw/ohT7c8Frmf6IZ2zNyMMCyAi4oXj7zsypMEvv9v+NSwehxPn8zeMB3MoSXnfzwElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwlAXuvSVA6UI2UOLCrtB8B+NXJCZuh8egx1aFSrSNtkAKHrIgXSBpZ99n2tRE1z9rphTlbxWbdATbiJpW8VrFCA==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "4C25AD18B5FD890D7151EF9F12CBBC5DC49B7CF79BE2A5437FE15BA660F7D11A", + "noteCommitment": { + "type": "Buffer", + "data": "base64:7Q/OPCaCRx+rkjjgRo3mirLyPr/NRnDjFKz5uIwdukg=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:QtmrusnKO2T3Ay4C2b02NKppz2dMwSnDtM+LdnwEq9c=" + }, + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", + "randomness": "0", + "timestamp": 1719441707431, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAyukXH/ZnanqTAxDwpA8M112/riPdGFT5eMgYNPGrnVWG3KTVTvGkgdVi4ax6eUulFJDnd1xoq07fl2NPEk+URLBeAotFQxEt26PpVh6b/jy0k5tfE0rJIOhncgVJMwf29DFnAjMEIAvIOLPT9qVbLNQEMY6pFyiDQKt8OyKgVRgPjgBZeZaWzzA0Yir78n3iPQ4sDzYdWY26CN6pe3Rf6kHh+HuVD5Seb+FsIggpE1mBhYABy9oK6GXq4qI65NDsLwsGoVH5Qz2VUMHlNZbZL8OC7ViGtasy6EbCOneuI+7kCGyB5T0J+kxEzfRmJvdZL1yag1RSx9FxWFRPmnXggMZPpSAGYKAi9gWItsWcv/pUgMK09I0KED7q1WxckTQ4AbcOnyV5mF6AzxDnon/05TU5bLVgcEVjQtcI2V91yefkRAsjKgsv7gKZTeX/cWhv2wIYzbUSsv31ZY2n6rbnb3FxJitBuFD5Fi2KRlsiLBQ5qhOiuGG+Vs0UPmdIWDvKUIOzJzcjb0I5ZUs3xcKE87z5KpBsvqIfM3h6c+BszNHDtHoUzJ73j6B8eLVgPTJ+dpdEJkaZZtRRI13wdZoJZft3Lg1LvTqmVRgbjig0Nww3z6DFAh8VHklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwOtDS2Qw2/8ZrRoPibD3/w6c4TUZuXVe7uVlcVxBtaGPLE6xfuW7jcWf654NM/oUAshVSibAfIoMA5XuFJxa4AQ==" + } + ] + }, + { + "header": { + "sequence": 5, + "previousBlockHash": "6C0FFCCB541112535C9D3F7FACA59206E97D7F8C03819F76DF5D3BDB16C37EB7", + "noteCommitment": { + "type": "Buffer", + "data": "base64:l1cZSZAZP+Xa3HOzuU/VQ2EEGfXgciHvljUkalQmZHA=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:Xn5b79Q4VamUTW4LCp0dFs5Amcs4q7wQ6GCp+KzijaY=" + }, + "target": "9201866281654531936596795386791503876274441021197252859723586932895305", + "randomness": "0", + "timestamp": 1719441707902, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 7, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA+HDDRgCSqtfU+unIAQmlR7lr+Vl15xGMpiJE97r6lA+xA+ePuQHVUR1+GV4Ipdc5hMz88i71DeQj7sF4jpZmhovomQuSnV/4+up8smfdzU+C5RhT6YEFTvp5z5bWxTIl+9wNboZpmNIGLUxLFbiFe/Unzh0V03ffsI3vIAikUQsLEsN2eHzXixufZEM/6U2Cr2LLIgwb/zzILagG432iByw5PVtwXD+9E6ye4q+8OXCvrZwCe1Sm/rrpTYcIEFrlH9ujolXdynOpfl2W1m79V85xv7myyedadpdgBf98hYIBfS13BeDgRxmUhIvxayp+HmQ6tnl8mAYvC23hWU4/Ep+Ox301S6rAhsv7bCIXZYGuVwxpWV/DZovCh8NCe5FmqBfa6BxaA2p+qnCAEqkvO/pB9NjaE2DJB2GfVt+6OGlPEYc5+UOpWxMFQw8DFB1DyJxpvHNxbVQ+VBic7cjQl/QeBKE5fxidJdXnGYfGSuuHZghBFkIZCJfrhMbcnNJuaGFvnNaJxfd0L8P+jND5R+SjEgjvJPh4IrqPKbMXG5v3ubYidIC48Md01wBQKOdYmdlLtkq7vcI9rcUdWUr2GbFvksNza9SmE1yGimVvZ79j6ksLp+3WP0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcsoJNdbrQJXDZlew3/ExS2Fo5DGeXnf99Mqrl6vRFFYbkJUuoHbR7Q835Da8c/lJIh0JTNV7kRdqCBsCSdqhAQ==" + } + ] + }, + { + "header": { + "sequence": 6, + "previousBlockHash": "D18C0BD715B3742DE3EE603FA5464013713CD3EC47516601CEB2F1C92636A1E5", + "noteCommitment": { + "type": "Buffer", + "data": "base64:z8pNfaYp9hR0Vdfjvcxw1dNpnVLFozRRKdUNvIZdhFc=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:ACdINcnUrHT649NvK9h/Ueb9HjNPli5Iedp3Nv9LkAw=" + }, + "target": "9174987784651351638043000274530578397566067964335270621952759689537226", + "randomness": "0", + "timestamp": 1719441708372, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 8, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAPqmLs3kEtsp6RDX27GEwYjJ/swB+dCIticFhUkR5PFKRpqDK0qtlfAz05kPvCEMeXTiuG4Ej/a+Bq4gWGIFc2oKT2nErneqA9qEfiESkRzyVEi/cjWAoGaWC0sHhhYQoJDKNCEwQ7CwqLnkjtFm5kdd5J2/5/wOt+KtPD7B/NvEOMAM0Z7vsc9SX8wcI0sJiHCMVeemCTG1+KEOJSrhI+L9gtdbWbGl8MyL6gpBkLpmM5YXEPgGlOliSUFUbwq7aGQch2ITMSK81YsTTf+7dn3up8FIROe7OVQIXcd3gaFO886h0aL/+z9Ef5/GGqkipIDJgzVZOHhRivXi7ZCSBEsqyk7QL9D723uYGFWDfht5tBDsGse2QVssYTOwctr0ThSDur6eFui9IV0qbm06ksd3DvwxGryg4UEwJEL+xBqp/WYhMAlW2wwSSqXHqqo0DjOQirJyvVE0b6xFWiDQk6TzgE6+EcAAgltmqMGcf/NSiuXWBk5EEAxTljgdivN2d7Z4R/G1PtOMUFrM1l3CdcfDmFPqQuLZ3vSFxqY9/PguHIP0qW9POjrk93IgKsq6GN9oHuq+/zynvjvp4VXQHEkZzPZn7c32mbzMiMBhbklAt3XD2DIIzhElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcwRDzsXh5cxHX35HkJ1kQdotmhaWO6ytlzJpO2q51d5Wopic26jC3z+8ZHtDu6ot5FKg+d6JvTL6gtXFuyhWBg==" + } + ] + }, + { + "header": { + "sequence": 7, + "previousBlockHash": "0BB4B6B6E56E8C9A863EA4266D2D75D79E23E28779483EE7337EE4F757B5F049", + "noteCommitment": { + "type": "Buffer", + "data": "base64:k3BDdZVJODTWmPWsUvVNBH54zyAJBTGcYegReQeHhyM=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:cWMk8WHQB6zLDM7ilPcWI09ki2ensf/wyXSMFjhf2x4=" + }, + "target": "9148187795366513087508709149025146424715856256637674150531751753357577", + "randomness": "0", + "timestamp": 1719441708837, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 9, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAbnLEHaOgEnj+BuzPXF/0zlnW7ZxZytlIqi2EsXbAkBCqmHLASf7uaDoyOH5QcbjMY3qwjgHuoTaKdCvKSKFG4Ak0A5M4NnC+PNDyc/mj2ku2IVrA1E8BmQNs5wgFWlOW3sy1OcnkE69b4ln1n9He/uen3q/EsmhkJNZKe/OftXkJB2j+N+3OBGWSUaUEbBh2gaH2JmXIKHdBEUFngu/JEPXNOF2iyQaaCIEFNkoe2uyxrwsULYDIYnFVR1ZMWrQ7MG3Po04muutSIXMtaky7S+AdCk2efHNJqzxFQRJaA668/pnQVHqkr7z9PLdRt32mqRS9baFtsfBJ71UZgOWgI8Jb81Lb5k0BNfWLrg/PvOLrAs9VhKqQxgffmAL3bY8T06DrAcIbM2L+ac9k10XKsRjejBq46jcbB6Jnzk22AI4/2+pqZSzltRAddtOFkZUmQvL9EMSEsy5vG/dqomndgeFpFNA3F47LXtmFn2vXaraiZbovuFFRGCBFx4GwQCXo5IPXEkRDU8REmx+pC4vuym3p7wZzBII8TkN+fqiCwSSt3xaemYD6GdKuO+LuzPInqFjhNK0/OLOKk6V3SIVVhpswyrk0UW9hQ2RbjsR9h+yXibUB4woaKElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwjUHz9KZjRvDfUpZtLjkDOjuDQ5op+Z0l9unsXY1Uc7O4Nv2qgAa2IDLfi52UMd8O80h1yzGqc5b07P+sKRtTCg==" + } + ] + }, + { + "header": { + "sequence": 8, + "previousBlockHash": "DD2E10F6A5EEE09B16680D21855369F2271D9FA73BDBC300FBBB891049F3D7A5", + "noteCommitment": { + "type": "Buffer", + "data": "base64:ml8D5iykqwVr05+UxsQ5DRamCVvVfGN3SswiLeCHW1o=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:2qqjRiEepseDBj20XRTF6iHPuR5wfZgMQy9TzmVtcqI=" + }, + "target": "9121466311864876128923245652724724632104869735746188813030060672759072", + "randomness": "0", + "timestamp": 1719441709303, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 10, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA21ilbjArKP+C4LpwOnmDLX7uf0OckijaP3ilua55UYS5eFH8tRd05pWBm0VwtWA7TsTogKdGp3v9tVXUWCj5H0Vss+53PruNDYmqcnsulY6hbk+2JT3SKLD2dZIPfwvPe6nAHcrhYQFGhVd3nHlayfjUvxRGYG+vpVIKBK5K9koNNCH/63L7QoVc7k90yH76rk2eTnAINO9qMTG1cxOePap/c2CTEOKUJCTrhzoixBC0U2NkODHHwJZaf8Z9kgGf/Tdwc/V6Uw+OillwSQ8P7g1BJLZQz+IQOyntED2VpEBoq71z1ntz9xhq+L/59b1OV0ZQiuPbBfJMLRfvlbQjmmOeYIv1XJibBHLUV+odU/bGuaZiYed5UQ/EeC4BZGJWgw/GJ/g6M2t5wujBVJdWZHdeBVkLi3PHr9RoRw+6OGANyuIGZAs9YEkrnUt7weViRmO14GLnbgJzlKnuTTRxEJd6scs6+LQJwjxKqTH2Z/kcnZ++uy0h8ZmfhHE/AqGaDc3roqWsI5KnEVa3l6hrdvfakZrIlqBlHVG3A5bJQSpLLgISA5TEKfvOXLCZgSeV+dFjGlK+/tONsneaph1AzzGEZWfcNP1NYsYmlo/8F68Du4tMTzIRzUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwzrU/k7Joho9/q9tte08Zb2lMIVC+Z+N9zu9NVNUgOw6Rrix1XS5hhpZMJg8Bpgstix4y5KecXbg8kBQZUI9kAA==" + } + ] + }, + { + "header": { + "sequence": 9, + "previousBlockHash": "68A8E7E164F55EAE6C472227972BC9F36715D3334BA410E856D0B716FE454354", + "noteCommitment": { + "type": "Buffer", + "data": "base64:A0EePpa2R8oKPAW/9QsU4IsShV/gXumNWosUa+SwjE0=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:lqyBehni47RhZPoC+o+W5yLgAxSsKeOSX1GGUEJD/Xo=" + }, + "target": "9094823328238119324660168503613036415495463326164889575918026009508991", + "randomness": "0", + "timestamp": 1719441709765, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 11, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAuWwvugJs406ahfgO6OdzFvIgr3h+590GcwHZfAL0wq2PgfQiAFCyU5/HKtALDLNgeAAs5M+M4QOIS1DMbbCTWvMOh1XKjRslqiADR2HzOCqjiSyK94jYw9HvhcIZF5njDk2chAyxaMoqwuHKDsEAGHcyTLTeVmfQBebnAe6NtsYKS5V+Q8O2w+FR8d/noKKHP3zht7ZARE7WcA0VU+0++mcPIW5BnNrfI3jl1uQCyzyLNAvXSe0oWmxPAzy2lo59hy2mJ//I4iTu5kfqhWZj1AVgQhRNGDe55g5Ypsr4o3ivyOZoZExuQd+azZiVtwVrbYzrWVj39Wo/W4ug1SxrQhKY3yCxXfT2C7q9rZbx/VqVwX9Z/ua6nCbvpQYw8NM3I1ty8CO7CoWyMpB8TH8zcZB78vqapSGfnsIXcv09fgXbpuV1Rqpp5qkYOi8wsYECiGS4TWYDr+xOPxEorBddzCGDRbvu2dwBdsX/RV4SjfizP97+uEP3OYelDL0Vo7j2ffS+EzRg/JdSuAGzbeYr3OgC+uBp7mil31v6KyqIXi3mJZPSyOhmqcPRCQ+/h0lDcU6vaFVV+fR5Ojlw0xIrKZBr01ILydtb4sx3gMxg6Dpb5ueEnMfoTklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwaoEq8vrFIXGk/vwRx65L89NP65JXnguMH14mcNj9aZjXLVFIcD8kvDnaGLO2yzsbFpvztR/MhA+RVc8Ux09bCg==" + } + ] + } + ], + "WalletScanner skips decryption for accounts with createdAt later than the block header": [ + { + "value": { + "version": 4, + "id": "d9a15ffb-857b-4b35-9cdd-f6aebd6fafb4", + "name": "a", + "spendingKey": "22b1818679e09c97ffd67ae0860ccaa55b2647bdc4e9c7124f7d1bda0f56afcf", + "viewKey": "2c0c02c7affbd92a2e0ebe4e92c26a40e2a75648edd6a3120e7847f3d8b022dc72611332c4649d178fd1146cfda95188fbd84c78476d617163b071a2115b00cf", + "incomingViewKey": "8725edfa3900dc75bd03d9b3d5cfa0660343b51184180e58325f78ea726a9305", + "outgoingViewKey": "b451d2f3e6f74f91db3ee30066d9da1bfe910ea92b96a8bcd894d4f08b6b82d6", + "publicAddress": "3caf65b0e6927c83b65dd4a4665ad94374bed54388eb75b7891d24ec52cef4d3", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "d6fa34fa12847f56e656c4053b6ab91ee02ccf0ac38390cbb0fa82361ca86d0a" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:t/nGRBfEM41bTDxqlaX709f32cBhvjnkDBuhenxUZFE=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:U/40oB03yqTvyY7aSV79yLUPad/shYB6WTv9jd5Kxak=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1719441710613, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA/esP+kmF8bkJ1NxWAyk2dmoqjlCOs4l2wFfu1/iCzO+p5NndvXVSnThaEUKtr2Rq8aq4Kq1mShyptH74ToK0O0SIyUN631Fcf6XOjVuyCb65EvVqj/8RvBA87I+m+2aLSyhl0ktuZMl3pQzPATdp8cML2HsUWiZCiTS/CUco79wTflqXlBsFEZSsjT1kilmVxwquvQt9qESHLb29Y9XWJnHSbrlwWNfiefTSvHrqVaKEaeys3SrVzyNSkvJOvHNPnpZdvTBLumnJfcViYjA0gahA1CPKODcRzPv820Zskp8hsauw3byb+xVfCZQGA5melUg9x348Eatp/5WmM3ZTRZ+61GOXhfiJVSeS+JluL7zkzgNkxG8qKxc/pmtGRsBrloy5KKtN1jY7BB9r7qqqvD0WAdXknZkqpsBFtJ5qeup68B7TAfbTBHdUh5vAbdtCbxoANOLQNrdKM7fGEc9f37KkJk9KucJdfAY6BYA3bK41jVZgBLzaA0tEAXx9N9MAVUW8Pftt+xowV3XrWzjO2G6UHvS8Y6lpNR9rc687O5ogLrKyvqE6pYXdWFesdm1VvE7E/ALgQxYPJMZRyVQaU9RXkNrUixVyo7/1dKNjlBgCg3GC9OyqaElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwrRTjhXSQIwdA/IwpRPE92hipHCD86Bd8uVV7UY700aWK5JcRmYYUTCBHlr3EaAP0tNqY6uH4T3xUxi2hMUzwBw==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "B69F5673E6D012B151A6E7F1EF4A49BF8982BDFFF46F5E15116E4D52163D641A", + "noteCommitment": { + "type": "Buffer", + "data": "base64:O5Ti0VZzOH2K5836j7oYVkvXnRyaO3ftHWYowlVy/1I=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:DIlzKFLoKriJ60ZF5Z9nW+RMbEBDl9nsXK2m+xubzxk=" + }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1719441711079, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAtNXI4O2D3UHH3OTDo/uka34tATF91q0lEAriziW4/jqpVdJKRQaod2TfnAR2SsYWJsiMNTv4xXatHuCP9xSDvKuKDLe53JDUBatP4wsRLAW3kBcHTNrc91Jh4Ixjc3VATU65ok079ONqvsYhQEOKEDW5FRosPEhnxo4edvpdxFgO+ScfLYdofprgR9Tca4lWQmrHCdt8qibqLTeK4MwEfTPAbn3Lz1nP0xU1PCoIIIOT0jBeAe6nhuh+GTSr9xxxezvMWC/XiS3SKHvOKKrQVUvZT1TcwWA1cBJdMBQQir8itqTArADqCF/U401oxKu/9SfNqM0stq2BW5IrReKKXtkIpsv3ZLCy22Ytuw14nzWxPUeSN4Z1VfRYVRjmpepDho5JxB/UCFL7e+P/oSZkygTfZ+0uKlPIyzRc47SGP0kgTFszyFeURXpbzzfkY+nukmjhk6t+TCbKjksYaSM1cR7ZQ7PL0Yx0bRrVoYVYTZlKGD+J6u07CzyUvWlbMhh1+EtH5nmi8zUtdUUVqNrpuY52jCucc8BqOwdUp62GL8UM8eCJxiUvJEnZSMDGndFP1kbLxCdhybgMwcu9An20LGOL20ahOeyD2/7uqrUKDW9tKEcVOr5VwUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwqaZ3rvg2P6S4roBKdX7YfKVaLgQxWauz1mqT6E7e2pQmDQhrLq2KOmLtGAL2UDIXyzMuaTnXwwUMgkd+xonyAQ==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "F8C3A577B51F46225FAE20DD8B4A69882C775183A262032F8B7739EF067C4BB1", + "noteCommitment": { + "type": "Buffer", + "data": "base64:eRIDTZE+Z87/uElJHpqUwrNlHDuCezdU3SN+XaAzfTU=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:5H/Mr/pypUmRoTmGXiaWRVkeYmFJveBYv/8DvQxh+KU=" + }, + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", + "randomness": "0", + "timestamp": 1719441711542, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAK2c0dfSgQKwQDHZWhi98pAEQKTri9HeXcVAST303romkp/rz0+oDYCPVrCPg3HciREuiW3d6upXvQSI2uCRLUx/l3vD7t2UtkxrRU/9cI1iw6QhKMi82kbQzwVVV8Br2Cpn2n+u7Vi7238UuH66I1nDnNtMBodKhsu1zoMrbzbsGIUwgHRwBUbGAT+e9RUGM0kMBr6VXCUgD/XCYpYJN6A/BIflVu0DRocgqqFyZbp6S/j+joJqsxRl5cYt1PQ1E3X0htjCBE4XtD2m8RKkpknRFVs09l/5WxoKRl2CuG2xfR1SroDKEk38Y8m1SQCPkpIkiVrjEgrdPSwMFz0mH8rYJ2iKoFSG1soeAT2U6svW81enpg2OVuya9iqmHKAEuXWlnruzI2W7ZXOc8/5SlIDUkbFIoiSongTOGsSi/LhL2xPVgLJkc97cHNJpSsv/qID+FBKN0skuHduZ7pMghl0ZA/tGx0dfjuwn7PVZwdhZVW2tP85o9WZl0LdP5PsBJtJTmR+J+A4Q/XXC9JOwodtqMZefQTXjPOyuhs+7a1Byw2oIYGwSe983ASM02a0OxH0IbKT0NpLtWmUvjMDrHPROaANJTybw0I+RQ3FXeF0sFTuyDxGoxB0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwoiMgzBfo7rCtYWvJwbmHLpmTA9hSjXqLmOblA/ojpWnmL98ZGsDxzfQGJmhhyK86xANnS9T5CHfUwFsGoUFKAw==" + } + ] + }, + { + "value": { + "version": 4, + "id": "c1f6dd22-e561-4f82-8e6a-9c971f15b29f", + "name": "b", + "spendingKey": "bf4b4f8c66d092df8a26a7011821f031f8ad5afe8b1fdc4acda68f555dc16752", + "viewKey": "589dee75144a22afbd2a397ee9607f5ddc375ff63b3b9fdf49c0ce73b6789f65ffbac13dfb77c2b254f6f1e87f426359609280253dce6867d59dd99d1c8faf0e", + "incomingViewKey": "469135ad8fa89d4673d6d1338e19e2e6fc8a6c15d63084957315b72229451b06", + "outgoingViewKey": "270b99007c639c03e1eef6cf30afcba910f9105461590e6500f7f9339cac28ba", + "publicAddress": "7f64a69fdbcb9360ef620bdb024b781c162c7319bb5a107a087dd13abdc09466", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:/ipYiaG2PnsWFJVFjVpEDDjM5lxOzAzZ4d8hkQR/kU4=" + }, + "sequence": 4 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "f23a343fffb68241b5fc6e287a79c2e65647d1179a3429f14caeb0062d2f2802" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:/ipYiaG2PnsWFJVFjVpEDDjM5lxOzAzZ4d8hkQR/kU4=" + }, + "sequence": 4 + } + }, + { + "header": { + "sequence": 5, + "previousBlockHash": "FE2A5889A1B63E7B161495458D5A440C38CCE65C4ECC0CD9E1DF2191047F914E", + "noteCommitment": { + "type": "Buffer", + "data": "base64:p3+mAe7ZXnQg0WEvnZ+ueOASbdoQy9rq1uT1ZCCWDyU=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:jIxPONrZpSh6C1zCW4eGEYHV73i1iEwB0NDY0q+JH8Q=" + }, + "target": "9201866281654531936596795386791503876274441021197252859723586932895305", + "randomness": "0", + "timestamp": 1719441712010, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 7, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA0PREpfnx1UrnX+G4RxRIE3vdgJodv+Gx6c8Wj5Hl3vCB/F0eRwdvjmvzJXUoAb0vH5hiOyE38jRlTvXTy9Djy/yAu6aZsSD8uMDmDYz0JPW13VFFV+uXEhI+BAr0yWlYLT309wTlGuZYTRERzlaAfJivK9J0HWmlRYI//tLbKXURurGgQg0ydm/y9dr+yy9SbSr7UfF4vjzRzH34PwK6kldpt2mjcXvkimOuQkkpmJ+xzWUHhP+vBtd4xZeFn9jII0kmSjOgHkMa7W6krvRwoE08HS2YdzycTbtNAQL0UgXrgjzr1gMcdPQJMsQ0tpNdOoOq4ES/RVmxBFGoU2v/nz9kEwuUGZitKQRwgE0cKUzx+cJ+vinOSQHCjX7EW+BvWy96VqbH2nEdFbQv3fIEcxEWTFl2JWZxL6YnQI31el6sZ/p0hhmLWnBTRI27xe2v/qKCr3FYWYf93idp5eUWvX5f7xVGt02YTDnOguReTs/NccxhA6ngIfo2BNeHr1xqtqYuqByks2Er5cnEjZoHYtI6+bDd/ubH2BFZtYei3YPbAFV5ek0utWHr9lg6xAdZrNMOzrlH0rZtQgN/4w8a34Pau20xBcFcSkXZKEzXx3N6AsKH7qFZiUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIdhdPRFQi2+z4y4df/EZISDxdU6TiHEdm47T18UKlGsrTwPVFjtibl3l4GX6er/2jc2MmRVhY86YxBYnKLOLAA==" + } + ] + }, + { + "header": { + "sequence": 6, + "previousBlockHash": "689314CE4EE1C3BBE6EFBA25F64A8596EB38F70F25BFDF6F1D71448D82409B9C", + "noteCommitment": { + "type": "Buffer", + "data": "base64:EUSZDapvMTqNwZ/wIVqbZ27l520PL5mcDpFjqO4J0Dc=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:wYTT4nOnC08v1j4QD2y7+y7Ltj/sLzRMAo0DG+H7fMU=" + }, + "target": "9174987784651351638043000274530578397566067964335270621952759689537226", + "randomness": "0", + "timestamp": 1719441712468, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 8, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAwB9MZEdyIee95Jkofyz0wz8rvOnpqd4WcrhQhDMGyuWurYlBXBftPylZZTDIpw3DWvRNKJ1kiaJ+fFs0pjlJCImf/meYPcbg+F4iQa/rtJCIYwaRgPppCPTQ4cDYvU63n8wANuR/TUXtp0xqO+RD7ugTe/+nO18p5zeQD2K6rzYJyefXm3/IA8oy8RlVhZhuF/CsgdhO4jlLwKn7xJI6uVrhsRmrYgZ+cZWAffK170ao0EtQaUbImLdhMpApQuwcYalLPvSuhZsmmXVVxw7kLELWZjY4QZgL6RoxR3A+MV7qpc9CluvOH4VKsT4kO1js9mAKyVKMFsaGDnxRRu8+LGacuXJT8CKTsT5huUtjTpOHy3YSDiIfW05QcVRWgCwFLQGrqOFpa/N3Tm4PUBAH5eOlA8r/JGQa+7wuB2yfGxVv9XwegM5aVWVvpua9u5mghvP22wmWHmMeHoIiP2afwtXtzn2VZyWqYcRidwjwGcMyqtGlEO3GefgFdjJ7FhtJMEeppUmk2EnjX6efHuSOMbzrWNCamS/qq0zXeQq5MHZkL4pEwMdYpe0/uGlZuZF4Dc/ES5cNM3M3SRrGQhFNLJ7aWgJzKthI2mobBBG0/n5y6vOY6lIe0klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwd+jPU9f18mMXt3vsQOcw7J+kn3dhQCpscxHOFR/KeC81K5IG2VuVSbddtR8u282W3qB4+Zd34WAD5renxxe1AQ==" + } + ] + }, + { + "header": { + "sequence": 7, + "previousBlockHash": "18ECF56F6B123864351D989605EC8FAB4C24BFD4393BB6FCDDDE54E42C8E47A5", + "noteCommitment": { + "type": "Buffer", + "data": "base64:VOICD+MfsxjYm5nKiJ5i26H8B5ij7LYE67TfmvIGnCM=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:hcYO831nU2O+SraZYl9+P1G2gPzqzcRE4xMWIBJq5gY=" + }, + "target": "9148187795366513087508709149025146424715856256637674150531751753357577", + "randomness": "0", + "timestamp": 1719441712938, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 9, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAzXqmfHoGn3bU85f/Ho32mrrGpf+a1i8zalQyF5dqFQCNJBER8uHVNIJB06fECw20Os742eM/qSXRTTx6Fb8ndjPuu7D2sB/SROe0yod2db6QtpMqpJ+hroCaWb0GW7bS7Za30ZOLkJ5guJESPduOn4l7qa0H5EHxnB0GqyG/lJkXRwFGQPSd6sFqXC/86GVvtVIaZ1b/8w9iD7svVmtHsGMe49XFeXGwzik4a57E1Ja3BIMtYhOQgwWxi1kgmHJdZ+RVUJnMpdG/OkxvuOsSfXYkV9U29+IOKL5RpzYdGzELq6npjoo4lnRIZIYuE8xs9u4go7wSJ2XRB0C1XchAEQfGb4BA4oOjLskIRhnzqjLGTORjhVDnwozg13QV3Y1z6moVGKH15E26ZWuicVI6iKBHWsUoy1Wdyfkl8SBAC7ABhWy3gQ8YsO2aW5rL67GqlSIzjfhuTFBkHbJjGsv0Q7lNGkAIUmoNNZ5HvuZ6B/bF14Yrh57kVIaJ3QofdLu39g3r1CwHy6Nciw1fOf/3fKggvJDAvQPMPnLVIMmnDce9vq3ur24qX+JIwAQYTTuWpwbDf9vOKBfvMqDxNXGkeuamS55yVeoVmpjiQAl6jK2HrW8nkbsaKElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw0eqROTMQ4tkbiKk4O0z+Hp1nYZIoviQc9R4HXTa/JNiUsJbdb45S5i/tUQWr1fot/1SHcn1z7LVtS5dNj3xEBw==" + } + ] + } + ], + "WalletScanner skips blocks preceeding the lowest createdAt": [ + { + "value": { + "version": 4, + "id": "0f3912c7-d0dd-494e-8b8d-f0fb45510f22", + "name": "a", + "spendingKey": "1616f3fddfd2ccccf7fc1ba6d43ca07ebaaa7da1cfed5b4df8a05aafeb5ea0c6", + "viewKey": "c2d1aa0928bd76503b0b02affb8ee9b3efc1238aa34f595da93d24acf0b1b03274ea6a1474eaed8f1b451eabe3223a82a013a07373ce23e060b13b489d6bbd61", + "incomingViewKey": "d92c1aaca2cfc6d8d2191b38cd371ff05113e8a881cb8fc7a5f780276d83ff03", + "outgoingViewKey": "75bf194d2ed96fc414dd7731be658d53eb695481a043955bdce568a5034e6826", + "publicAddress": "669c07cc167c86b1b6cbb66dd3ae7a6a4487764d134defa5ff05c8aaeb15f1ce", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "1a08001b02f599ef90837fe444719105d73e8fdcc0f0c986a5e27a14b8524505" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:n+sWWQKt9PbwH7itYIFkNooevniZOIfYBzKE0+xGoSI=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:xTnWIr3ftgauGO15C55qB/rZQSdUmk9t0gFa/WD0xds=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1719441713706, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAaSjZbAIdjtuTTaVpUn0OR4V8vg5kicC0o1hrMufop6Kho3YexaBPGIarOwPBk2F1PRukFnGDRV0wNtoSYkGSo85WND5oQCDoLzUlvBd1mzaXs8lSAOU//4uait4ov45uDToS2tldx5nEymYIOss6fimjQ/cdlqsJ78sOOTZ3brIJwIxU4lGLNbyiFpH4u9GKxD783SV+YZHVajGXdYNngvYqlxKJOvF5io+6McKWZoyka4LlUFtIK5aanCVYzAJShZdBvajRZXeVHkHAa+tqyafcdDnNx5Q6indRxTuQJnl4zla5xh2fNuc2tp5IHpeYr3bApMGEGOZ1G11S+W7w7vWJ1ZkUwP3I8ME/UA2sIgkmK5tIuwPM3QT4yhvdY0840nVFs5XE63/a672GXHa1OQMirduB/ajEpfwnL6LV1lj/YLt6ooe2x/AIp8wsvuzdrRvy2M+fUlCnq1tJuXRfD3fl4yx6iZIG3LwWE8A/zr6C/RUWO6hwTAON5+dSzSSi/hBOcDA6XqOnQ/e5I8lwFhklECpZqdfR9bOuo/zdM13wnEmayBU/yuQfQkGr/ynFCAQtcSzW9z1ujtnC5y0hUGKcH+xpV09IgKEpCH/nSbCqvB6g6wwNUElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwjk62f8n+8CMYl8g8kq6FzMdsckL2QLK7rUji+URQMm3FoOMmFjiB/s+r2h4wJ+45M7LgYOKsrhudonvGuTujDA==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "0E8FEBC9A909790C9187E134B4A60AF90DB372D207886A2232FFDF775E43227F", + "noteCommitment": { + "type": "Buffer", + "data": "base64:s3Z9V1u6TfkPU6X485a3RY/cnkStRIntVC6kx9pmr2I=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:npxn/zG7ccyTfS/RVk6NqFxXNTAiBmDp9uh+4KyoZZs=" + }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1719441714178, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAMKtT3oq1JfB+hNdK3rhdddiav6Gjul0r6/uhW2xqWG+oLDDwd1mtek0+3VH3pmV4IeauHL2+A6B+iZd0kEYzcZTkv0NyR2i/nFwR4GwgUu2ywL1s2r2BdLgoM16l12Ihys7IZMDZbfgZCGWBXzWPfvZXPyXw7Q8BrFwukeE29bUOKBlfTSYgaQtbmNpDP9obaj5by+Ld4Wo9fuJlBbawRK/13rI696XGxV1XvTdj3vWVYQQ2OmN+8qRHSpo2xiCg1fW3N73VpM2UorqhwVSAT0kigxxXpoRtXrl+FvmHwjLECmIZQXZ78D41fdn7kO6iyAkyjKwh705kmVCLbDqXH40JMwzCdxqbHnPsXLFELLGcVKCR9sY/JH+/kX5444sVl5d4CMkfOSlVwWXanBFRBPlNgQ9lZSaiQmXr8Gcm4RrziQQU4Iqj7piTxULqrxiTZZQBXrQAIh+W0In4QaGmhnT4efW7Zj6ri8Pliccvh8aicXuwULB1KUUmpYVvZ0286q8chbb4+7npLGwkrdxf8J1ZVill8+YTEKjkujMOAjY62F/9ahe2tw52+Hb8ZG+oAe0SULRoHtHBNN3efdygB1qetUNJLf1/I8ccWzGz3GmBeHrBHMKHIUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwUiK0RaSNlbAhO4HW4JAEMFCOektB/wIZcp1LCmsiEgpGLT/xfgZ2J3Jl/8WkcQ1JPg2QlDWsXVTVffCysfekCg==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "050ADA5533F338F1BA0C69A22373CC1C1720034401038699C5FC087FA41F8458", + "noteCommitment": { + "type": "Buffer", + "data": "base64:jFaBsVq1mcjZ2booxT9RGj8YlxLixkSETDInn5a/dHI=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:IcQgSI+5kTae2zkZc+OKhqL/U9tdCz9ljq2w0p1Cw18=" + }, + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", + "randomness": "0", + "timestamp": 1719441714662, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAwpgPAEyajkka4eeMl1Tk7vzRxWDNfzQ80jLD+sl94z6BTuM0npeeAz1butHPEloBEStOgU9GLY+Yz7RZL6Tw1uapHzy0v9zgAWJ1fraC2zqZglZN1owevbu4xrj+mPawJnxHifmDmANPtNaZmeFqJP8gMzJ4Aq/eNPK8tbr00scXoqunHXbYvUdOX58xFRGa26FkoUguSVYf9Vv3WI00ycBpeNB7xrSmG7L0aqgHswy1DcCrgQN/6f376sfgWvGOn030p0T4U8BsjDkts5LnOnAmDpyKhDSUzEuGW0d4oC0Ds2mP69qH5VZeUSBv7nTg188EL/IP76k+MuviK+exc+WIboGykNdBnFzXfC+4bp7FetmJGcwYHpxjEZbCJ6wohCysB7WcUEdB1keMTtlLi1nLRZRhiydnL4B29nWvCQ9/erdiNdV813a9MDxHJ/b7BGLLY6WA54A94eIiUuZACDFWX5oBCooplTRo99JCf5Zxz7Kz8LSWN6EqqNd8BiVuvyk0pe/v9VbDR4cPjJT2hToWAcD5eBdczRbe5wEjTBc5ZAccKncWAUCh4nRMVKORg27mdx2AXJq7ugkuodJ5bYr7ntBDgnIGsUitIcIB76yKP93XaU5D5Ulyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwh+p9VR4u5B/UFJ6w8Mocg0H/QdLwjnL8ZafcCM/Mg/NcOn83fK5gKEKkMDEjClA1eclNc+T6v52xmbcIZlHLAw==" + } + ] + }, + { + "value": { + "version": 4, + "id": "2ab7a678-7f34-4cb1-8a0a-bec779f935d4", + "name": "b", + "spendingKey": "7ae31eef18f3a40596fce5062202676e5dfbd3d015a674ff76857fd97b9ad623", + "viewKey": "75e1defc921bb4d6a2487cb79748674ee91a2977f93779871280a6b37e9072a581595f2e115fd38dd1d994600fb8fb412771ae1672a62056462b4072d95a756c", + "incomingViewKey": "effdc9b669b507c5c5c6ff4a024adb3a067c0c7a983cfee72d3863289cae0105", + "outgoingViewKey": "d2a463ef226bd1a539ae966e4486b4d73b545ea427cdd57a3c36332fb745ef84", + "publicAddress": "6cdb2bfcb2befcf5dba27e11dce86bc37e8c0f8d028c816d462bf580014ce92c", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:AjZ0RgBBNBs4qIs7/5Po/PuL/08a3K8UkG5w6p6VNmA=" + }, + "sequence": 4 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "b3ec5c5ecd614b5ca21e5b1d19e2967d3e4d9b46a2f7a596b3973a5df932d709" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:AjZ0RgBBNBs4qIs7/5Po/PuL/08a3K8UkG5w6p6VNmA=" + }, + "sequence": 4 + } + }, + { + "header": { + "sequence": 5, + "previousBlockHash": "023674460041341B38A88B3BFF93E8FCFB8BFF4F1ADCAF14906E70EA9E953660", + "noteCommitment": { + "type": "Buffer", + "data": "base64:rhCy5v+aagBLTFzuRQLjb3qxjOObCYAxsLPBlf/++T4=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:P1UY1e3sVBtSfeNG0CIizxZi2crCe8Lo+30reKwJCxA=" + }, + "target": "9201866281654531936596795386791503876274441021197252859723586932895305", + "randomness": "0", + "timestamp": 1719441715146, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 7, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAs54E9HWNTTiXFcBvkNzmyp562MuJF9Dsose2yAlsx5S531ki6xZ7opTMKPUADdiIgrw30h5xiDdZQK5r5XaJjqK+HUYia5Rtd/HTCTbxwvSyV1zl6S1iMLP1kJQHmhg42Y372K2XQsJy5wk8fDj0gCLMqZ8DvM8FvipBRMLzwg4ACkc0iKce6VRCzPr021BFY8hbrqDCzcyODl9KXkNe7Nb8f7KaN+RPF778eFiUNkCNti4ing6qS4v/Z4QLiwdrm5h1JWh+RCCPOws9pf5BRNgLYKpv1ZxREHNbKVWKCpyFMGDa4Zqg3MaKz4mb2tKtoLVyQcHL7Me7ja1gJW+k11zTvdmAAzUWJIU2Dybu9n11HajhFh9MxW+magWkp30tOitFdzA4WlT2h0HRdWNph6iGRjgZrtfdRIZudjnosTB4pAe0Vujx+U9VDAB1VliMtFRYAqO2E7RTzSs+1IqxNNEvfxTV6zqro6MfOl5YbZOcWSxBMW/o3kS+B0xE7R2cSamOG0bskTY2fMFZZl4uwE6sCs6PkwNjI7Ta8jAFT9YWoaOBJAX7a+PYZjxuXPSN6nXMJX4BXzo5AwS1+jhHLkqY/C5sQwiUkmaIxO5RwKLDpE309V1dl0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwOKaw1frd5RHcG7TER8VH5D8xxpsm7HMr0AwyENLbFpmpxiwIF0Fg1/G49EfilNsX/SlAvDgB2rlaRb9maiJmBg==" + } + ] + }, + { + "header": { + "sequence": 6, + "previousBlockHash": "C9669A682E4EE5610FB8750FA51020662AF29F6811ECE62785088AD27700B8EE", + "noteCommitment": { + "type": "Buffer", + "data": "base64:n75LmFooqdVeYYip4h/WFM6VFxCZ9OES4nfZUv7a52s=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:93lLj1s7YLC+TooYjqFjGmn6zgoVpIF/bmdX8RgTfIc=" + }, + "target": "9174987784651351638043000274530578397566067964335270621952759689537226", + "randomness": "0", + "timestamp": 1719441715604, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 8, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAj4CiLhs2IHU1PkFZKltmxULim5udHt8s2bh+Uv/UU4uou3DVVEiH4lQdvRw9yA5zqnAOWmOl6oBsRVgrKn8fLcwmOiZsXl1bgrlUkqGPOEChrHJVTlt/qAF6xa9PuxvIhgoLBMFxEtORRDkC+5RckP8M3dvIBF3+3cxTSSdqUBUKg/CUNxXaMgVi3S0xU9y1nTOYEu4sFtVzNYNjAY5MmoICcWngxxnQzSkwqTVurbC4CxW/42UbMFCR/LlgzELYFJpLhxgz6jR7Fu2QrAxu81860jWVOs4OeeWiCCU3p2++jyB0Hcweu/3IP7/eP+aUm0ecVRxaiFXo9cpNGoCoSsbV+/zKv/MLnUd9QKIuNDBb8/n9tAEeOZUt/e8VfpIMBo8+eSTUbXmybS0T8slHq8ORVO1TmFDcN7wsT0ED35vWaUzIk9Otn1vpnAJibvmbGlFUp7BpLz3J6KH54ImmLqqb95aJohKJmqeZFuTkLy6N5DRhtN5Q88cQhMz3R5+Li2PZy8NpYLlk4mJypfyGYhiky0LqEPUfI91Bm1Zx3NACD1VBlItLYBoI7LjFrinEHs1xxSm1AVVJxd4Ckktjsl9ILly9yH6rZplmiqBFbvGKpLgkxIMU8Ulyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwO4LoCRuzeFt+cisoqjfFzzGZ3yDmDtGicWz35r3oF6UCZbUR1RIHLxN8Ja9on7VzfEm7Fn0nwdTRYW1HfqRRCQ==" + } + ] + }, + { + "header": { + "sequence": 7, + "previousBlockHash": "A562DF2A70F3C895A03FB74102E9659F9900F8B54E9DAEA04CD087ED24E44755", + "noteCommitment": { + "type": "Buffer", + "data": "base64:Z77o6+kbFLouSQoNIerXjL23qy5nbYjBRfHsVMiDvz0=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:hfRtvGkUUTRcbaf1+ETs7yEs8cp0C2feg0RtAFwtqD0=" + }, + "target": "9148187795366513087508709149025146424715856256637674150531751753357577", + "randomness": "0", + "timestamp": 1719441716107, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 9, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA4FcrJ0yOAGHyKR/q+WLSBtoBXhWAVIJn9kUe7zTPpeyBUB2CsG2Ikrd80KVFy3V50rocDc6Vh+Sv0KNNquREpsOeYmarnlUTf5HGL/Vvt/O4G1vj49j9N36GgGwCptsB8KqEYYS8W9mxsPVZGU5hn3IpCLi+7U4rkpcJFPDtgsAXoWBkHKatWfiYm73gYHtIIG4mtLJPGz1WxLOiDO5JMvhl7LeqJxoomCPwn2ZkCPWw6uxZ1WEIiXu8IOHupCzKehp3wJ8NjegQch73s6UVmxLiyBuH5cutxC7XsHxmJtbVfdDUrL4sF1x1o3O6CSTgs4JQE9h4IK8IbJyofpRZYm2f/HyYDdFrwEGgy7Ey42kP91mAOAykEiyf/YaUrxg7gJawArtUqeX7BJEWxITpvv86uClQNHTlyv66TgAi5jYrZltV6ETHXoLgvUK+KNC+ifR7iXkOoUEjtSVurff0OmAf2nixpYA+4MVXbBKS4oxgfn5U6rBgFxLAm3dqLbUMPHI5b17NOEMfhGBfaUfVEzQjNvs0g1N/2Xamlsv+V9RTkqPqUVQaUlnuIourCuMrwHpAE5nHkfKNbKbMNZSeNKjcPT6lpPI9m9C8tQXSMLMVAh7d/yAD+0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwCT2TGuANbPq8HuWQmlT2a8TnepbB3DCQAZ6zLH+x1CaFcRCSt6NxlfLDzrrMKMBGHgYp1SmO2fhIBnf/sQNtBQ==" + } + ] + } + ], + "WalletScanner restarts scanning when accounts are imported": [ + { + "value": { + "version": 4, + "id": "fe28d0e8-833d-4a92-9e3d-ed6208fe7667", + "name": "a", + "spendingKey": "c9d732595166f6b8a4f285f4df0ee76adb106b0c39615895422a633a08035d24", + "viewKey": "9518d298441f85255980a09aa07d8280d159e77b892b39c88d8cb8d5854f58219775f94ffb45bfb9046868cf82683acb24995d7f1f3d2216323c9403ffc80c04", + "incomingViewKey": "ea7dc790dafb32a077078d0590313d0f1ceea22cbe9c0145fb7a6481c1693603", + "outgoingViewKey": "c702677769d9853277836e270ea232f1e81b4f804074eeec27a1da371e534379", + "publicAddress": "bbd37319139d42a222151a92f482b48e4381f48ae1cc1b83f92f7b278c57d3d8", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "513fd18713e16aa52ff01fe6f9b9a651b28088e229c8e1e050e0507978839306" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:zesnAHOaaPkl0/TO9DoQ0ZLtdUyoy+Gm2q3p/pSWmnA=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:kOw3BW+L/nRPNpJDVjsqXkF5IH10JZSCpi1bFUfxsHk=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1719441717120, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAMihdYOG31F9KdpljKhP1NL0cUntNX7RhcDH+BUxV0+WxJwyG9GtxEcw+kEvnHUl2q6wntoA3jgQFnlNmeClscGdiFtM0VxUvqI4uwlVvJz6zO3OPengore7jvSZujc5CKRgS6NH5BLodY+6wxL1+ZyXx63H/n3y9rXSAWrbfXmgIbYzwhdzulg7ePDE6xAl+Zwtcy4lAIas9i4bSM6rnkqks5e71WWqRHbIwXvZeRRWqAmZlomEp67dM1u9bAXcgSQOQQU9KCC2Rw+tLuGiniATTfmo6pSoMV2ICfyxBU84/VEnUuKuvIyMMXpN16Y2T7hZ9xyspOgBprgwQ4jpiazeYo5vk2pSB1MSm/VADqLTUI1Sr0wjbcqDRdAlc74RmQ1QFbtVesTtY2pINnj5UXJjTU+8SIC68r6JkHCYAPVNWihG2KUYzE5IGRuOLe60oi/ZzLtB+kaR4eVWWWXB1Y9cPFO4i1J0EJdYQb5JYeYWmQEUhuQ2xhiSTb8XhilF+rQBk+brdNRau0xUhDJz0ScfQreNt+C5pVnwST4n2chz3IWt797PkmanYGNmKR/9F4+B738nfzbdRCKfUS95Gm+lVO+UB0LWosQCbRiNiEcF1a61D/ChqyElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwKIcU6uhqwIsl+OCYnKU+YJ7ziYvkYXpF/R4xr3hKJCHzZ2MaSsenna91kR0alVNOeQ7ujnmUXbZjMqZz5poAAA==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "C8CA8E6A876D5878FAD70AF8B1FEC153FBB8A7EE0BEB8FD1F734C2B938ABF1A3", + "noteCommitment": { + "type": "Buffer", + "data": "base64:1Rn0AErDH/BbtTxnDaBwFo60NbkEGgyXjs1d1aWy8i0=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:uWKhSe/jmCtM1SuBEktGCP+AE/nY34LoRQN1t/FgiVo=" + }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1719441717605, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAUkStbxnJv14xGa4ZJcqzts5BPy8iqSUhz4hx/IbvP5+vA059uEDC9nIl/AusFM1TsAcqGkE15a6pLTGKgdMRzeUhXOpuA/Qp5hOXymRz0RupSSU3RNFtlpZ88VdnjkTe9PxAanWEnja4cESktVTOtdcShFtSNU8YRbVwHH5uI0sEEDSwpUKirB5rM0nlfFZNPyKuklJ05YCKpWAUmNPUq0DYmVRfOBfcy7omRFjzCIyJiVFs1CmK2FRsFAFtdCD4ikJwTumlgloqThMRFamNjn70hh9HgoCDpT+BhWUVB0AFZothSQpRoSjJXyD18s88M2xYpFRZjYfT4g0pHxYwRq4vT8UYnEPoU2yBDXPkLj9b92fALPMl1U5aQCDxd8tf3aVIQWAMlQMWuQi13EY9NVh9YV3NaBKaemoUmfOBNQCZsQcNuNCAdgwR/xCAPuEr4HaE/oR6wQZpBhDLIEOcx0UcAaDKU1InbGq98r//iWsklMgFaAWsIxQXF0FSWTN1WkcvgHDCxhjJS2mcxjXwRJ5a0d1moYW6n29MQIjXehiZpPJePrE7xnK6jxqfI7HGPcTesBLgUAxxeztAyEXOdG/R7VenvRp52s0MFn7/PiAje7GlC0RGVklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwsPgtteMzJa4nKsHJfHj/MWEdvBeejIxX4C+ppoUraAN0PwnujqDeIKppcl+de6Ht1FdYzwh6RJK1kkXfxZ1EAA==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "17D18CBE275C4EA79045FBB9A0152C81C5BDAA792D9412BE14E00E12B5550FCF", + "noteCommitment": { + "type": "Buffer", + "data": "base64:0BqcEKChkWcfXhBfKdW6MhM6MQRy0FoFf1erPAVQ3FU=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:Y19EIjCOwps4v9oLCvj50uAqdtqFSeKa8Xj63zIpyIk=" + }, + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", + "randomness": "0", + "timestamp": 1719441718073, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAm1c920uLO3/jJmQCI47v9aMbzLLCyH8K+QA2e51OIWa489yxqWBZlY1ftJv4e6rq+kjzI1Ku3pYAHj4pzE6H/Xunvvp2CoGYJA1OSsOYFlKzGpNIf7Roy26zLwp+jevTRc9ZTy3N8+1KvpqWui4Qbuns0KLBsj64bKGaS2qtTUMIJ/VuCkpmftHkTzAYVrGmYArEASuOjnOvLlwThgW7O/gGItUJV07lkjQrm71w0lKtOnOJsnN5xEfwoVFs8OahIp4NATJFEyvUaybmrF9FRJColZ/xHS7q0Y6aI8wQKbLEuuFrcXEtwriK2IjUMbTgMFXJak0HS2t4CQu3vke1t2CI5edDd1lq1jMgal5/K/l4Yu2LSeijdUc2rV+Kd9ZXjod5YnlejZIEP+3vkix1A3JHruU38pvZSct1guQZWymBdi3eqHvh+/gCGgQdlNZhbA00teKkiDDVPAS72y+pwnJP5XasbMdol1YKLkjK1xDtl6IJc2cDBrmoqiruC0kNIhyp4qFRLC9pvJtk9I0n7FWYBYwYBfEU958bpO3avxxZ8Qqju4bXmjy5r8prYpPAdmWixHqpwo3+i7kVPh6IqEmlMNo47fkqLc8N9Df7uqtDQ5qFYze6VUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw8WS+j73JJrv0TpdwwzTbigj27kjmsDVuLwjhWJxG0ut0MPLWf4Ihcme7ZLdzipDDuh1eRmeBXx3h/RuPmvH0DQ==" + } + ] + }, + { + "header": { + "sequence": 5, + "previousBlockHash": "5B39752EBABBDA851444F03E035BC0B6E04612C13C50B82A8CCB145361AAC51B", + "noteCommitment": { + "type": "Buffer", + "data": "base64:fR9+wSJRKfKxT/12apfk0tNAgy+zDinHornWgrFSFQ4=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:3DhppDVoL/8nVjdNcxebZOU+iEQrpY7LDZs4Z9HeN04=" + }, + "target": "9201866281654531936596795386791503876274441021197252859723586932895305", + "randomness": "0", + "timestamp": 1719441718552, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 7, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA84sgWcXoPfqmoR7nHjM155OIkQqj8zxt0TOatCy+KAmYlhquVcZjNKiSWHH9Sqrg2RAcL6Fv764HTdxRq9eTG9ofRyrR5B0jCEAgKruELDK4fbgw/w7WihKUtmVeoi/7kA5trExOzUONhHqC6DlihZOeyyXaSZsxTWOlMGP7t2QK8WplmnZ4mHfrIhDFyn0QJfgKM9MUD+sDESpBLVE1lhXYf6EnrACz5oDAgQ+EX0er+4pugnFW9vyZWwIR0a+8qF9pYz+Hkk2zWvZNfkYjMMrIa4hDgDTOlEdlMiRmry3U3gsKGk8fMoSmKW5Hqdip8RkzSpQLpjHdtxZLOj9scrPnNQ7oly43dPx94FWQ4fL7MwHAZcI5dz7Jf9xnF6Ru0J9D2Eeh2z099QMVQugUd9ps7oUz+LOMcLMu1cdcC2U15hywzkJnyDb8M1q+DIXTdOqvV78MxJwf3FZb0uu8FxMCHYkCLD+n5mES4+lXzARVKuPGJ+EKnfITmeoKqylaCuQ2Bx/Xtg6QscKwSCDhgAn8aEQJQU6/GuwBvJ7vl9cukJ7JAqg/IoLkbRyLfq29s/0BTT+4yRDVY7JEoZfNsBQWL0aHh3XQB6BOHNA3XrkLJY9Ow4EaUElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwHKzxAbQx5rHwq9DRAumevsJceAwH6j0INM2nyXE0l5k9vMWzIiA+QRxWb/vEbg0zUFCL4tmtZmKEpueHOUPrDQ==" + } + ] + }, + { + "header": { + "sequence": 6, + "previousBlockHash": "2E9D77A3CD1594D60F226EE0ABC1C3D71DE2E5F37EC4F768B438CDD865378B52", + "noteCommitment": { + "type": "Buffer", + "data": "base64:AZ9SRuU38eL+Y3fG5n+4ojeU30otj/iQZL6ToiPEHi8=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:3+eSpl6evErbkDzWw2g1cwEsVwpHL073Kl6lwvRjMJA=" + }, + "target": "9174987784651351638043000274530578397566067964335270621952759689537226", + "randomness": "0", + "timestamp": 1719441719027, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 8, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAXuppMbOuX1P9Nb+QEj3LUtC20xc1z4Y7hGdhOXteKuaECo5jbIGFOGQd12ubGWqlkFf4kFHszm9u4I3ICVKIvpPqND5YmkXij5qqeNEgwgKC55tSykuv9eHIM3fHaQMgzQVhlnrd6ifhYCit3V6JFa0FrxXcviSZ19uuiVufFO0Q+Rsp7nBYSc3nVIDTq0PQmxaNrwrWOkLGwFBmvknqC2LzLKIs75Zbqo98HymeGPKQ5mIAwPzhoqf4/oqNS2hwozrOrFayLiM9YJrxnsCII44iKi1RSfYWa7kiYxqnpDREyP/uwaX35Q9bnu5gkzac3d7EUHgJjhZPCTux0uRrRbY3eT7cJny9NzSvMK4mx2r5L/2ViuzKme9cdogZrScaMhHrM18mpNu642M+DM2wM+hmB1X3gk5zOhabQoyYg9MKgWTIydyq1BWvqooeqEQUcNt2UDR0oYdmE8ta4bn1MUTuB5B+bBHckDe8jDRdlxHuWlPccw77A8mjFuDHoT2+u/KKZd4vpGnxqIKs3vv+kDvN9zp2dRQhJIFbCssld1jIhFVS1cQEt7W1+TbkPsGlcjg8X/b0PSz+KCuNEHoQ2sYyxXOkdTOwJqIlO3quegpu3Ds0+UcM8Elyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwh9ZL5A3gdeb0gUiDRfzGnIXygpFPj/BBlRsTuIa+9DLHVZ9C7ckg76VIIDsdewZHWypZOnKirTJyVTeDoclnCg==" + } + ] + }, + { + "value": { + "version": 4, + "id": "e456bae9-7fa6-4bc8-a07a-df3134b43499", + "name": "b", + "spendingKey": "3255f21950748d71ebbf2c076fc83b9ce7d0a36bf20f65434f59dff5710640f5", + "viewKey": "c42287b519e46eafbbb030babe8f51ce959098806661a3ed1835f8bcbd726ab78414c529999900bae72e13d829c6ec915982445df1fcaa7efd7c393fa88d0357", + "incomingViewKey": "5ba98e55e02563842dbe144f53295a69befdc21a9295e606f66a068dd0063a04", + "outgoingViewKey": "3e8256d8194cad6296eeb0efc3b4069b3eb6bae6e433923a65d677d077596224", + "publicAddress": "77b8690e1d2306024d5c2d715a61e959d357799a81ebfd109f12257515eb9d73", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:+pRJwuyUK5S49lKeCQlMuoe6W9+noGtRD7+S/PseErY=" + }, + "sequence": 6 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "0362ef3d9cf083545988a4229b6021f77b9874795e6142b1f01c21cc1a61e90b" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:+pRJwuyUK5S49lKeCQlMuoe6W9+noGtRD7+S/PseErY=" + }, + "sequence": 6 + } + }, + { + "header": { + "sequence": 7, + "previousBlockHash": "FA9449C2EC942B94B8F6529E09094CBA87BA5BDFA7A06B510FBF92FCFB1E12B6", + "noteCommitment": { + "type": "Buffer", + "data": "base64:wJvBtKKtiM3i7C+pOQw3tOjSwKpAJ0B1fLQ29S08N1w=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:wpt/AHXDmcSysIT52HZwG3R5Fb6qXd+A38RDFLVT904=" + }, + "target": "9148187795366513087508709149025146424715856256637674150531751753357577", + "randomness": "0", + "timestamp": 1719441719496, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 9, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA1aD62FhUOYX08jpTsSGet3Xw+/WPICIfF97I0ENKeWGHIqANRp/qcVvJ3HfYCWWvdNXrDdM9GU+oISWfuGXsqDwZz5ZkeGXyJV+9jX8nn1+rJ3wqzVmpdFXPArMCvIkA8b2wWIzG2Gtp/h7oBTkF2c3XBV0TFp0JXmdSfsl6L2MMqCOy5A6vIAPTIyvlpqSRN05wM1d5iz8yJfvvT2mpSD4syggBvH3PizIlJARWw8ezd0h/PabLS6mgNSdy0JGGNe8eI+ViDJNbpaCJBU3BO9FIF7zLTrbrpUslXdIOj0qnJGd+mjV5NzufnpsCcJ1TyitG3RzvoQnucWEriCHq2Btz5vuX1KuZjDowe1LIMRqtMyYO0tH8+Iy0Hg6YXgtIspIDSjkGAloto55Z+CVgVuQJEjiMU8DGERB4bf3h4kuC/da+JzPCPfcTh9W2Qphm+1C43A1lkIJlTScjAuFvcaRfVuragRIpWssDL6xkgoyT1VjHTMs7+ushaLGAqz4pq1DntKQvASh77UZVQH2N4keJhYmuIXdwkf7Fxe/Eou8uqs32Gmf7Qmg6PDIDNKptEeh70cwOkKrUPFU6ephodisM2mBnQLTo3YSAqByeP8gDfVXTPUTcnUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwe/hyyBzA4hnSsCXpBnk09GP6mRkhhBpOtANmUldRXJ7iotke/xgANc19FA+lTyojHHduyzv72chwzzplWjgJBg==" + } + ] + }, + { + "header": { + "sequence": 8, + "previousBlockHash": "310A4EA8214CC2537225A980D0DEC08BC9A8A9831D63CE2B928B06903F8A7B59", + "noteCommitment": { + "type": "Buffer", + "data": "base64:o1UlnyKvu2N77NW8IkjtH0dH8lr0nZnqhA+cGwkurxw=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:qaQSGOM2f+O+IQnwWwTephTeKSA1/CCrrQmdtuil5SY=" + }, + "target": "9121466311864876128923245652724724632104869735746188813030060672759072", + "randomness": "0", + "timestamp": 1719441719966, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 10, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAoOwj0D3ax9FG+clbD7etJj5uTK5MiID03p2Na1JC07SDCYd/EVLeRkW4EqnedD77jWZNt7VvtGPi4vnTP8vcy0G7aPA5RL0A91qIZdrqKhuSyu5wVdx84XSAB8hcm2cLLfu9650q2aQZNxXlx3uheRwxOENo3oGW40ZBvvREWrkGjyIdUL7qUYC2QkN6u/bR7f8IMg8rKz8EpAUF2clTrbLI/FTR+ADe7B2V1nD4gKeYcv2nrjjxzdW/nkJxJGfD9S76UsCjSiQB2Oy7w2BB+mQVEHozdf3kC0SSVQ9xDYgI8/OV7oYj3nf/WEqWZzjQhk0aDSPcZJmHc15pesL1NJ0e/9l78g7MiE40rsHc1bCtzTLUCmUn60NO/tlxiu0eIWpBme266FomnDGC1JEArdyw7LCBmLKbKbBvYm7MW44xGPtqOUQ5kK37ny6ZVNt1SIEq9KZAhmqKtWGzP1a5mu210axjLiP4ley3/qDrWI/sHYJfWYQiar2yH55agQ77agxqhGXDF86Tpqu0SJ93srH32DGEsaex+ocTS9wmRcmEoIvGaiamKqEiBRpEH3T9C5b2UxKYpwbqutHJRhgXM7uZ4QUiUXdVlMn/EQGwp5sISQAUR4rgwUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwvCow5y3rRpVL2N1EBcSDL9A/Iqg9/ChqFM2OGwgCAXJ/GHA93wZdyhahefLwTobpHAUjIcvx8bjtlUh0X/0/Bw==" + } + ] + }, + { + "header": { + "sequence": 9, + "previousBlockHash": "D3F62D4AA18D107C962B87B0334AE09C65879473148623CFB3AED58BC7E207F5", + "noteCommitment": { + "type": "Buffer", + "data": "base64:cYtqOqKYIXJxWqNDoAe97uJzPq9y246RZlCat3WrZh8=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:BF2xeNj2jEpOOggwo1ZnL0GqUYJM3G2hV8oDN0/+Znk=" + }, + "target": "9094823328238119324660168503613036415495463326164889575918026009508991", + "randomness": "0", + "timestamp": 1719441720458, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 11, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAtIiNhGQHI592BD+2jwj1Hux4xU/1MQRUZgJuYVztJIODD8v556Mk/6/4S9ukVaexxVrIfvAYTROVMcb/drdmU/wul3zeHT3ixNN0lqzEABiEcpTRZRhVNjDmlSmE1F6RB8Xmdjz/afkGR5UB9nnzHxxguJUyM+2PF80dmuiAdmgTEyYhesAfl4o7nkKPABmE90st1gOs7+zIGyxc4/fQ2H0w9iq/GWicrdiCOcFJ+V2qzkEQRbqPPUnZnBHjINDeVUCAnzhGxseYJwbdDMytR+s58eI44qC9to/8FhreTWmsB+VQWHblCbOsMHHGQ99V2dBPilX0rqswdPB28KE5Z5gGXGltQ+PJeferK86c/s7EvJY+IF1CrPXTzrHsfaktE8D8+Ol0twKjQUE+Jk9MDggfQBJ1MMq50O6OIAt4dtXVGtc0HaFZ6TQBSnbmpzHzMeIslDuL1XWw/QmRzvzTb0WL2Bl6bpu1yx4MG0DKfKFEs529HOfZqYPaeG6VNkwFwPOKIkjo3czg9fn5SddOhr9kgtSfK9jw8j8o8NUZcKyyh151a8t9UjNBQVwSQacHFvc0mMdSOJrrvaDPFTIW+8kGBxCCNbFcfaqyvO+10ktxMTq3HOFp7Elyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwao5gAd6MisvctVeYZ03+D5IQh3WLQo0a/iDliETlqxzeHMVgoT6BuA2WpoF3kSXFs3hT6Qva6OhyawvDl3DlCw==" + } + ] + }, + { + "header": { + "sequence": 10, + "previousBlockHash": "53EB267C5DF1F5B5072C3159B66F8D1B50CD168B375B59E3FD6E6A7BC565D490", + "noteCommitment": { + "type": "Buffer", + "data": "base64:ev3+y7hDnHIyE6if3ebI3ghqYLww3T5bXm8n7eA9uDw=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:t4ybCJL2S4+VF6crnGawfekJPE87oU1+DODUqlvdRPQ=" + }, + "target": "9068258834662928698220540790897658244352076778286486653826470223999191", + "randomness": "0", + "timestamp": 1719441720930, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 12, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAE6GygXOf4j5BWdT54nOCh1LUxINfXl078ml5S+oGhU2wx7vinzi0qjBA5rNzwjLbTqA+76y6A9p0kiC015v8f9GmodXeBtIYgbvgoHVJH2W3s4FYdZvB1EOSuJ/tAmcLQPFR+0+yTwVPntIqRdcP0aeeWYDYYJQU5BeXQCdS15gOsREn6rTQBE/s4SN71KqX6/kfBXB6dDOpxDiyEQa5BSkEn6gBIsXUKiYgNd+IO42twxIGxhtYyhe6RpK768kEij7hzkdxLX5iCVNPieBoduZWDlj8S+L5ti/04TRdvFCP235VW7NhrNvptyi0sl5IQnQPi1po0gkIBg/HqBspWzFnzs0YPCrUYeny/jbSetdvBgW0adGw2yneLxD5STRvtcilpInmVp/r4pdn4k+dRckUPcyPgMiLx4xoGdr+88IvghXNortlt+4VspSNgWdLjI0cevUfkXTfaJABcXZnlZOTwiDyk1+H7IJOHJumfaYGYULKGG2TAVbdUi+nedQMEMJS6Z4SSsiwdsiapeGPQ3WbIgUe7DWPxXEw22mnkAYRqaXt4nzvc2IaEXLTcJLYqSjMHRCE0Z6qewZ1jbzwxxTdQ9DHcOXyPezWm+Cfe2iPYDs0bjEaBElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwSIDJaB3bHEHS7BP8+JxkwqnH3yDLXK1NCBN6pT1RP0MRIlqm82jKO0lN5VOuSv8ikxtE5Fj5fS82EBnvrPDpBg==" + } + ] + }, + { + "header": { + "sequence": 11, + "previousBlockHash": "4A82EA2E6B86275150FEC2C6AC1FDB1A2262462DEDFCE722ED151E2A5D974A76", + "noteCommitment": { + "type": "Buffer", + "data": "base64:6FzMIVYGtIqhuJo1jE5SdOhYSqPvEoYdiFtHCNei3D4=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:wmh124Szjs67KYcdnrL53mz6/+x3QXeiEqTSs8MxNYI=" + }, + "target": "9041772817458669358631436925553476123971485443441062513642264290171806", + "randomness": "0", + "timestamp": 1719441721391, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 13, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAGo2d4Xh7XjUHD+Z5PP/YK1fMNt2Rm54fo4Ur6DRViteRWqVhbLy+3PbLLA2r3GuBThxhgcK7rv4plClp0NJndKoYWCq6fMzOo/Jp+W/YGVmk1q9591gptLOXoO1dpBYtJGHDwvNEkiEBxB47pYYRfINPfql94z0W1M2Sro3R410P9+pOwQ9JYdWmWSnCl3txn1eEmYPNVPFcGbsd8ZzhOhkIDV7qSTCAxdoWSAKYiNawFC/sRCxGYWHArMbTJ9aqCnNAy+OKd7xokz+4XWNEVuncZFUSuEJjrtiLsbq8qUS1PFCeak6KBWeMo1ncjTNLQ2RCQXnqlQOud4BjRIsN7nMyOQXr/R5LUf7OQ+wfWmiVsQbHBfAsss8KIxN3lUZny2voX9xVxStOadO6ETC9KbMVOdmVKpVwlmSd0c9bEGVaRBGaXNLTeLHxLdpqgwput5rgqgwMiFQnprT3snD5e7tBJ/yrWpo8lPuSjjvJJOace6d+Q/1q8vkcVFDDzlLTIDnb5h1qygCxfZyZW7RmM3l5HcLn6vXBUG4JaKC2GW1LYAHdCAMfhIIdEsQgRCl3jz0s6g8r9Ao+CriifmqaWuy2hWwmTHL2Ajgb6CTT4spj74tEHUf34Elyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwu3yIJiuBIErbZCJyytdbtIVAotG6Ri8FaPKaopuUp+8PlMMYf5C1CGqJ7jYnM1XEGf2Zap6VhPbpSsA9sa+LBg==" + } + ] + } + ], + "WalletScanner restarts scanning when accounts are deleted": [ + { + "value": { + "version": 4, + "id": "5608f677-7560-4083-b0e1-c0a3bc57925d", + "name": "a", + "spendingKey": "dcac9d1a7fd7dbfadd9bd43ab3685aea9c5b3b74a4d7c9ea1cf40070ca7fb998", + "viewKey": "606e9b1087636b5c49fae5c3895de7de284e279c1f483cee1cb8aa61998697a8159675e7626ba5b6e72571753245a163b67912c632c1865c0ad5140f9188740b", + "incomingViewKey": "a7c09bfaf0cf05f6612408e7b7b8b67b10a35bbe66df052af0fc0cedad26dd06", + "outgoingViewKey": "792733ae07f1adc6fe9937578ef5c4c62684a11a3d5c49ee74554996fddecc9c", + "publicAddress": "12ff1c08a13d088f5a32dc32856c4802fdcd456d249bf598c1c862bdfc512107", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "8ed1d285981ae911bcd04fbcca375f5e4ca9cfa556e98a6d1645ecef721b6308" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:CWScPwTfahtcIMboVijiRAd5hMgWKrjBrcqXOcyYUTM=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:kuoQg7iqkHOZbvaPo4S47KRDH5yAp/FntwfS9Qip60E=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1719441722383, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAZnIqq8GI+k1soBWMVkwTI+IF1w8RptHRWvDsIVXv1LOSG15YUnoylKYevcaKzyEV2wxE4fxLJah/mZyejjt1h1D8JYrJI4pWZ7PZP24uHWOGJjtZfI8xo2ci1FdHJbaDwXPRaQp/L+Vh4dkvu6pAaia4A4TNwe6p5kjt4PZOuVMK0XF/ZYaHC8AajbbqWlQ2W9TNhwDa9UTD+jLExW1DF///HKanqNVsEkE48orp3sSQ0xn8j2AoNsvjA0NHY7h750eUv/JrypqZzxGgGlK/Uas6ySweqZogvmcevTgZTEjYtrVK1l7ChwkK2fKWpo1Pn1Lg0NtNt2fqeaqcZJCy1V51zZgZ4GcG+SMH030h/PKPf+88FMHk4W6UDVEGBxRqiyDo2LzrWI5OtACMX7GWjVmXREdwYtcVWW0akUdUHMWGHLiu2LB6Gx6VznR+75ksPpBxGWB0c+/B4ctP+aeAhtvCPGZR6w1ILh1QnxT72rmn1bfTBNsJ8nnu+zml5Xrfzg6/4XsoZryqto6BR2lAEjilGdHzidQvuPnG44WPHEgHbszzRucDs0Usk96uV3WBhT+fJ5njuBfv2HD04IMJpHdzFEANjzggw3wi1jSHqUkltjsR5xX9xElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwFSb9Otwas5kPt7gNIWpLZ+HcswCkTmqOjOZmWm7SjQRSV3u0wU5t2gIgnZwz2J3ghl6TLwziif1z0XNzvIfiAA==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "466643FB4B18A594EF5DBB72ED5B4BF5ABE28D2E1EC1D21751AEE7D51FBFD15F", + "noteCommitment": { + "type": "Buffer", + "data": "base64:zyTGIUK6jeU1Ir1DyS5BTmP3ldDdnxxqTI4dZNkVa0g=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:LFCORxJMq7CzP89Fk9vHv5iBkgXlcetC7lcy2zUFi2Q=" + }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1719441722848, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAB6XmNKp1IY1aEURzAqClfKG/fy/G4zdecGyD2x+B7Kelb9cDyxG6fMF21oVZ87u44EfCCKQDjrVvO6ORC2aSOCGxt9JHO+DJrmL4HGJqAwumHFVtVGgO9p9Er/p303UAB7Az46NXKB1+8sE43C2bNNhgvw4gYxgjU3d1PqZqs08TaQEJLLpX5DU2xkAAkUfhDGkl3w4GsgiCE6wcjNLHIedkqnmNPnhrLUeqJCSSut+xraTLuKPsFj9F9eBNWlAic4XcT4XjG/iK4zvY3DMfTKDi3M6+zpqNDqWLU4fMY1afTr42eqDb5zdaS898Mf67pML/5agQ6gTkhCDCSTcWOHrpyEJVduXbbarmEqoOAVjStgpz7bT2x+jFwcc9+WxCCBoxydOWK1oAABatXfPp/L49tEqp7laroroRjhsDjCql3ZkIxgw8itbXFrX61Bc2/KODWhhbwT/GPwc8p9kzPlk0XCTC3FroB9ZX6A6b/gl1VExsArUFNk2TRqfKqZjuB+3oXCKZiKqQ+k5QJrYwYsuHCMZfeuwzLaduPs8XjuM5rkgLAEPtVUjPNWd66KXymp6xzcF2jbBOGED52luOjRwMe58wGoKYcEGVw9fCmFiBU53pS6nvfElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBbOg6xkDd2TUpT7SGMFhk7mVfpTs9qNVhPvgkmJBG1O+Lx2kh9m/ChxDDn8/KgzIMyNkRFr6DsusJF3EXSkAAw==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "B58450CD35422E5444F91873E650DC3F2CF8E786579285BDF92C55F6D6E08173", + "noteCommitment": { + "type": "Buffer", + "data": "base64:6qloozS5eLcmWGvV+SkTYcF5t0xSQVv9zk4Q/GcP7ks=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:bOgn8i0XizwLH4BR6KZI68hq/3tF2XIZkFzXXutixwU=" + }, + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", + "randomness": "0", + "timestamp": 1719441723332, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAPlTDIPH+5KJOavtB06clP3o926BhqmULU6lOcwv4+xKArGOM7GDKRSnUNrFxzyC2TlIcEHckCBJ/1o4CKyJyEjPA3Z3E7A56Yw+ho4TPdUuQftQpaJCIawzeno96qTDhSpqfVVK7SHiYLScjTLyManq8b+KFBeO1mK8M7spJHPMSR4sy4ZNoWO16DcKWR8S0RAaRE+HWarUzn8npKJ5SWDRy9G1oDPrmzJyPNZd902uR65bvMBzSPsuDQ0psttmvbDRuGizinxELPAD48x/uD0yWPRNVsz3KsZIoPteRyYKa22erA8L69d0FYPpVuNgl5Yh5hQ2F6cUz9DJyOuUNWN3hR2FC0I+NuD8CMQC9MSJ0wyOu9tCSEWFNaWHNPlkOu/+hjcCJRBN5EJLIpFuHMUZVcJW4wKJqPt94LRIepAFHzSPVOy0qsgufLXIc9QOqk8csuJMEv+iQU4u5PaFHBihVvUwS2fIWE3xT5LsNFC494509RrNQkH/Wk2yj+UotZn475ZcNpDyhsUoDSZazc16O3ojcGZ3WVAHvN3/qi0QqV4JILiMcxnocyimS5JgoB1yaeMK2bGWkND6Bw+H+z8eBWnEWEO4dXEZWx5ymRVJOX06UFa6TNklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwV1zcPBDJn9ojn0ISFZ/oT/1kYxVbwU+D5ZC+aP+tj4NaT6ZN4n/5PW1OG5OUpfNgnr2KU0VvBOvVtYd2tPMaBg==" + } + ] + }, + { + "header": { + "sequence": 5, + "previousBlockHash": "B482FC6360EDDBD188F0FC05287C2FA687583C062192C3737CF836ED179EB8C1", + "noteCommitment": { + "type": "Buffer", + "data": "base64:Wv+VfZYw0Y+eVslZb19l4M2X2CdLiWm05kjIpZ0qyic=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:FRBKSxosajxBo4ELpBQCL2iF+f1Jc8GXYGhzqRAIgww=" + }, + "target": "9201866281654531936596795386791503876274441021197252859723586932895305", + "randomness": "0", + "timestamp": 1719441723791, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 7, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAgcY9hi1n/ejp07obbwNB+8IEmPjRas1IH4LyS0Ru9FilZV+UUZoxOkonNJ/MqV0BUqWCcNPsLN4MW0WUpCTYtQDolVa1p/ZrC4BAXhPl/s2Op0RJw6WbaErKgq9V8s9S9HJAQdBANSEEZzMoiFoszDt6UMdw/yhVePJqegbcGqMTKbxGq7Csw2Q1d4SD/SOvcVHr7db1dIthic13H80+JYTqlWv2POgM+o6vsJp+9HeLSKYGAfCdFZivDyCaKmjWR/uerw3Fd5Fa6Z9B4hgaTUsg0BPKs32rNBXQBs2+42dIo9IoY9vpbvpFfJ4mtMHyEIcQLbhmLaBl+bTWiRC0BSUjtsH/wzRtwyRQjkwf+GAOe5BdlLmUhXFOABp5idZg2hnM3aglQfeSJkvJVQVuBGCffH4XR8fiAYpIRamQwtMFNPHvFc/reY1Qew8WmlJ2zNNo+2/TGz/fQlqyRR4TQ/QBby/LZ73ZGRBj02/6tpmHNafiO/yig4K5QPod7/w1VnSQef+c10U97dHxa5FhR+cZplJfVCHnA0hq32l1RoY9G6bFISazQsZdzCq89OFBl0vZXTKryFPGpcsp2OhJrmY8+lMaj2sPahGnxF0OyK8ge7ptPrU9eklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwSU3pHewJqqCx6vPFXUijDU4+dVQbkUIvFcjjo24Sftxyg3QBH0k38MPHtLE3CtuzDZSMzZbYPT7/lupzKz09CA==" + } + ] + }, + { + "header": { + "sequence": 6, + "previousBlockHash": "92BDAC53C9E46C08D28E7B5F42D873AD7740D9DA8E6671C658E4EDBAC4D65F27", + "noteCommitment": { + "type": "Buffer", + "data": "base64:ymX9ZhiQmvKURrRAWADntPSogpuulpZubDclLilywyQ=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:lQXSmilggkyT+uHihSzAP3A49aib6QfmFoEH0O6s2aI=" + }, + "target": "9174987784651351638043000274530578397566067964335270621952759689537226", + "randomness": "0", + "timestamp": 1719441724261, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 8, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAboCjeRJkAvi2tcNO+D4UJNicmWF0rozhHdMyFknr4pKyXkDINjF013wrOvy95oZcNCjU/FXlRvLHcnfNnh+fe1NL2Y1dUYG9rElI67VRe1SYlnmmG0Rvrs95ObuZTnoVTp7v1qj5r18834JV+4WXBIxWee4n1LCnEoy2Z2t/zOQJPAGV7nrzpMw/xs+zr8bs4GEl+a9VMDcP+foCEVwagTr8iuxdlSnSo+daLAbXZqK0n48re7hkaxq2hNK/zGsPJ1MS+VvQIvO/+nbR6IK8Ev3mr+WY6mX531UIFJXweKfhQzfGIKjtfRgX0jWvYAP1WSa/WH/No+cGD0JNnW9EQxJEQujJyYWq5fe5zPEaYlaf00bdr0uENHPPf/Nm+L8NaUF9S2QS9Emkv+mQkE5QP3mw1klOMYIe5pg51mesXPJ+uCxQKCi4v7b2PsMRGoXJMu+vpIaELQpw3TTt00VlTS7/8u57sQFsIiXSdZU2uWbcYGFLMsaOCVL8FAi8xbizqkCmncEO4+c1BDt4IoJdcCEGd8AweW09Tc05Wyn/93KqMY+YcArebYFKa8Ox62V1T42Fh5IkuPjmXhK7GAzU77evl03TA/VRAuA2BtmgPrFLnXfcCXl9/klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwawhG1frCkchqhA+Dk+hMmuc7QhhB/Kj/P9PriQwAsghJhUWdWXRm7y1rLsOaEXdy53klgVh598Jlk12bBCExBA==" + } + ] + }, + { + "value": { + "version": 4, + "id": "d3fb02e9-e057-4488-8ee5-776e98a95832", + "name": "b", + "spendingKey": "6b56d56e3307f99c16185bb7ae745a2c5d5e2750296a9c006dd6b8f7bd668d09", + "viewKey": "9125d6ca8bfdee1648ddfeb582e3342dc190b8b01bdf9adc6da7e8729692384385b00b181f670011c76c16cb5e67bfcf5bd9441410da8df7fc9cad095d9edaf2", + "incomingViewKey": "51a727e16a0be7e2b97b7dd528b4231d27346ffc10e01ff95d21fbcf2d042605", + "outgoingViewKey": "f9f3036afc899e86e59114786107175f84c55a23c9c1510e694b3a36e83a6959", + "publicAddress": "bd5031bf9cb4087f3069771013b389ea777cd9239fddb51a336b81273f044658", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:DB8f5WPbF66VwBmmV7BJ3g/M1epyx0+0vepdatNnVwY=" + }, + "sequence": 6 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "17293732da2ea50bedb20d45d87054bd8a5612b989f82b6c34e09c857c585304" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:DB8f5WPbF66VwBmmV7BJ3g/M1epyx0+0vepdatNnVwY=" + }, + "sequence": 6 + } + }, + { + "header": { + "sequence": 7, + "previousBlockHash": "0C1F1FE563DB17AE95C019A657B049DE0FCCD5EA72C74FB4BDEA5D6AD3675706", + "noteCommitment": { + "type": "Buffer", + "data": "base64:N3ATvm8lufM1rk6wyRWgp8+BM/aVcpHsvGz2gJ7kfxA=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:76y+Po/GE64VvnutdpKl6U6B8GqPi3eBb6tZFmRGkYA=" + }, + "target": "9148187795366513087508709149025146424715856256637674150531751753357577", + "randomness": "0", + "timestamp": 1719441724731, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 9, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAlSavJP4InzBX36z/iM/PlcD/t9MYWTVQWgL6qgLHaBKEX0ZdvSdDwZOEkAimcbkmeJkyhcbBxv38bS4wlhhlPINKRWShGyrkkx313CEImoGGpPSbklrqORWwwL5j39WWrOfgMm78NkkkAsoiYBgy8jdW8+uD/5FW0e7XoYzvyGUX3M7OeFVfaaJY6mRTi8oSBezVZyAevBJl5a3vsN7QyZRXdtNT4fQd+Dt/zDtBe2+m7fS78GTI6xKkvSrHreMscdK3/gUOvDibX64BhQYAmNS+nvY7/ywapHngJuRa1UjsaOm5/6vKV93Njx4mH60NP5Ld3kignMVb9o6MEeAfwLLSMkzxwVNASydKSwTfL1M/aIz0XeOoODyEAcPw2D1UeOBYf0Lr7i7wVvniV4XMXRMHcTGdo6tdRy7LvV6ziogmHFPTd8Hb7ovGINqots1msah+kCPs8JNuhdEuinFdsi2ExUAO5SX235lENWya8//aRNRti4IuA/P4OMVMpQ2TPhwCEa1zP0ds+vgSUmzHghazLLPRj2bdrQPvmKkEK6WG4UgcPUDsCNlsQmzSHZn4RVUQM3Zdj3BxklKiqFqWvh9q75/41g+r9qzkEwEUjEvK2Iv6uG0DBUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw8GB2r5+jcduJ3AHZAccMM/gKGcxPuMQmcovNB9efttPsR6he/57tprIY0uRySZ5aQ0mzHhYzi26R9ki8uiiuAA==" + } + ] + }, + { + "header": { + "sequence": 8, + "previousBlockHash": "9BD5C0D1F722DE36579E2FBE9961B5E1A4D2C3DA6B77C5195450FFFE7CA6E7B6", + "noteCommitment": { + "type": "Buffer", + "data": "base64:p5YfW0brP61LtOloOp9jgWexJy8oNt/KJ3g6Fw8MJ2g=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:HCPGx500t1evPWdtEc/LncU5TAedjob8/sYhK3RbIvY=" + }, + "target": "9121466311864876128923245652724724632104869735746188813030060672759072", + "randomness": "0", + "timestamp": 1719441725197, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 10, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAArP4v5qoPhEx1Ig1ccVMB+sStk1ZxSkn+QTY3zSYickSRv7SO0KlXsci//yVMyH0Yqu6oLmOYFRAhhwOQPu4H4HhurAIGux+ZnlXAM1d7CaCkNO60wcOGxxo6nSTqblg4Yg5TTY4m+y9HBuvjQRvdBmR1TJ214Hq84w1mnjL+k7IPmmujJLPsZtTarlJt3UYebNMSLtqPqabUy5emWwcKNUD7P92st8vDZgxutrXiqM6lhkQTL7r3jeCngaekzkbH89ZwKVFoBIa7AR+ocVUmvBF0Wi8NtFt2SO42qzWufiQx+hcezxKLTtYFdcOJ3DasRUp9cEPv31cPw1sC61D3hOVpwLvP1P9KU7u5q6JL9uIW3vfgPBi6Us1HBOyGAR1bjlFolSltR306ys7hXrJU7py+Lmpyeiy6tEycfUuHP+r7ApnHKSqW8k1sqSIxzbX9vY8AjYNHdWp6bwpSNBn59AaxlBejMY18cTI9jCL1OJLbGi46Z4PvYzMnKPVixHyCWVkoAAnmkKQsGXGZBDrY5EYRXfERxkun4mrzo9NlFr5sOiG4TWfsz2B/FQOJRVLB3Mx1HIHXJ4LP6jlP4+VoYPcvRMneXvY19m2jhTmz8FggcHSdzPG5QElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwkqi/ZSTnMp8qNRLXx55SbglxuBMIxFoylHDjfnDxQTMepPKUCIPZrZAUUdoVp4apCz43/7yZ6348ShDR+j21Aw==" + } + ] + }, + { + "header": { + "sequence": 9, + "previousBlockHash": "6B4D69F0949383DB8FDF24297DAC2425532007FDFD7A25ADD90676435BA1D86E", + "noteCommitment": { + "type": "Buffer", + "data": "base64:vDbEB8W1fxR5mChrwiqsiO9K4fbk1By4Z9jpMz3WugE=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:/Q9bBFoh2VEVU3hCwwyEeO2ApkAxEJ4MfvsxVDq9Ftk=" + }, + "target": "9094823328238119324660168503613036415495463326164889575918026009508991", + "randomness": "0", + "timestamp": 1719441725665, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 11, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAfz5rQhRv2LBNplPaQ+NoyVfp06EWxT06seFXZOF++1S1iWKU8HyorFHN9Sv8Ht6MRTlL/BJH5xlPxk2Q7uJGUHvMWNYrYpfn0VlgODodVqC5+M2qGyrvKBdbPl0m9QLjpMwMerjGFQMsPgSTTUClB3nAbYF9lVwnFE+9EPXhQWwRBY3rlpJbZK1sNgr8suuXm4KJ3phM/ADfxEbwYkO8S8stBvGAYRQCxBTt9lpetnS2opy5lRj1mcDisOqbgXNPZWhqHludnnr9Bmt91c9KmbJwJlSyTVqtOJ4AEhoH9SJLH7rs8cKQCkJvmbGsjW4VGxraiH+/X2EjT+IEnE6xCBcTc+nqiwo4VePsgc+ingnsBw64nTGaAQpGnwdIqb5pAxIOm/rrdpefaTl/60Qmy40/KnNEeM6wLWu/gMwLsioSCwu+FCBqxtcANgpDfPjct0xjEHkhnJmMPgXKdq1+6ck8HWYY+EYWJ2/iE0+Hl6j/tnQITJQ7N59LICPv3ZHoyMBzh/IGwD5uQOb8JbELwe4Q8Sje83FoDaxNVoj0s/c84xBqO067bsBh1QkoxftJKh7HP6XcWRCL62VgsA9UGDFOhcZ+O0HVyvPTTmDC4kcxl6XUUpk48Elyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwDrM4b0FM0s6fmfR6ipUs7INRo7EsioJr6Xyl75ONFu0WvVglWLtZS9YgjLe9iKsXF2AGORwu/LTou2A2L0nZDQ==" + } + ] + }, + { + "header": { + "sequence": 10, + "previousBlockHash": "62A65BD939DAF14EB812793DAD1D68A266B0BCFB28C1AA9F5515AD0587B278A5", + "noteCommitment": { + "type": "Buffer", + "data": "base64:30GdhXpk+n+fdrFVsXASqdE2WONKm/R/nrN4Vz3mpkk=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:Yj+qdmmsGqfavNNYPMBJFxm8SSQAOZSRxHm9FtlfEBw=" + }, + "target": "9068258834662928698220540790897658244352076778286486653826470223999191", + "randomness": "0", + "timestamp": 1719441726164, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 12, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAALPlrZRN+QtSZwyPJLy8lyUnHIvNRT7dPZp8KCxZxIzajyEKRYomauhJNnI9RqQCe4QuosEBFd3QufrhusPFx9FK7AsKGs8Mc1uOe5k2vg6eOLVTjwAnhn7k2Lc39xmpSvE8ofKFOPGai+PFIBuNx+aJnJUVY6hhP0v/0ENrex1QBxThKWyBV+4+vTqSWCXTO4SBAhmnXtox6wK1ZOmaq4HJqimB+fP31e2VBY5sFEHKCq5BuoWQVgCY/1PsU71LLJkRmCUwYgAWLfTwVfF0EwOlSVs50TG5W/rhE7gjzUA7dOhN+4jiUTlpF+VbBS6p1tzWAgfSJtyBjTgA9Bbc2hR5aW3WitwAzJvBvVB4v43A5CE4OFEBSFK/LFyieCfRguWcu4eTjSH70QoSC2oPArO0pFicRipSTyDCRcJgLidBPWVjwVnzpVGZAeZ0tmsdCf+m27orjKo7KWiWut9gn0YFOZwsDKyLWRKTrNHcgCWsysFYjM06BUC5Amn/1BvQCHZWUgi3IFFJNiELTf9XnhTS9F1XnJjjZ2HZWClSQnhT2W9x7o29v1yiv/gfZg3l3ntNtF9IRMe2Zbs6kDSk9A6EyiGHFaWVuRRZTxy545FGLR+J/d03dHUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwyQwNLQepGN1OnDu1Muhv2MqHQ4xFe/YRgZQTNXmGtUOrbJwJL9QPT/Jvd18qPZtuE/Q998PMgc/Ku/uUnAc1AQ==" + } + ] + }, + { + "header": { + "sequence": 11, + "previousBlockHash": "0697AD0245CC3C719CF76286F5C3240C079739B00010E8F0F56B33EB7F115422", + "noteCommitment": { + "type": "Buffer", + "data": "base64:pTTTACel4n/TI5E8pC4OfkpmN/EGiE6uCOF7va/nVFE=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:SN/R5FVlbTQjQ4zdbX50e3lYih0ZJS34ZANClBCDSFk=" + }, + "target": "9041772817458669358631436925553476123971485443441062513642264290171806", + "randomness": "0", + "timestamp": 1719441726654, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 13, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAcSX496FUPqOo4TjxtcTJqOJRxy4epG6ODKzmXaPDiT6tqmamSHBb7AUPM4Bt3cE1I05zqNs+vNQGGskW/gkVVp0JB/Mbfg0S/Q9fHx0m/fSA2LwK1kPnu7qAl+QSToDygk0h8PFn5wCh/TCN83X+VYEtSS9JStlgHHmcp3RTOCYQ5OEsn6ZqAiHiBMbvN7wQ1e8ZCoMU1C7vB/bhahqh9XhfJxuM9duvpUzWf1ABl5SK9ymQw3Lsa7pOw6BFwtl0ZzK+L136imutpyTZ4IMDT2RP/UYFAqQAN3r1DyZ9z+HeyOKC2ZWiJ8jjuqvCcANG62WXTQfhqHsZ+6DhBTb9n4KpxqBsxx4afJz0382XxCRRAlu2A26s0NWtUnFH+05HF1Ho2rjojh97JR3V59l84nCWDJMrO9aTTFeNbG01fSpegIUqraPG+afinlZKufkKOM2W/qOq+i47c8eLlWNrdpSDZ+w24qoCyQdW5WHYJOMfz2eKd2YvphEklqK7pYRmp3uXRQicum971Rs8qIo4S+Hze7ijRk/pO1zqmfwkzw+0XJBKDH2QejItxWlUnq55WwFQjU8seMN84dSizfYdn5FBc+yltI4xOj2CLAd1iU1KUcBOB5kpIUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwx3/YX2hVCm/eHIvDALpWilSatlN5BqfDouDTE+VaHSDD4m9HNKVwZpecj7PF/AKo3fBowJcylj+YwAxxvg7ZCQ==" + } + ] + } + ], + "WalletScanner restarts scanning when accounts are reset": [ + { + "value": { + "version": 4, + "id": "1d470276-d2ea-41cf-8904-079670baca37", + "name": "a", + "spendingKey": "31f0b594e6a695a50328c7874f9d23f72a4fb297d49f3b1512db5969a7d0ce25", + "viewKey": "004f4b700a6548829f2595590d5ac8c1be1e8dc306bca774a1a7a1d7c42c379864fcb81fda30fae499a587a80d3cd7c5207c3bf2c90c463898a1de0903c51712", + "incomingViewKey": "2854702acce3ba91d33a3e2853a718d9626f5b37e4100e36c657f8813e2bce04", + "outgoingViewKey": "0d44394d2615186e9480110ae402b81e69128ecf1e3c3c462b2cdbb323519328", + "publicAddress": "63193f684d12f5f2189cb682334aca7c19564f0400b50258b43eebd7875bdf34", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "a01d10fb10b31e4ae723892a440753e3b8c40ff23b6f635de2f8fd915109e606" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:o/aMntQ2IGPF18tp3O2D3KzGHRuLXZTqgQ+SVBflPTg=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:WLUEt6WpH5tQWoWuTDEgAngpK3UuO78z/jOIiB0JSO4=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1719441727667, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAZ9WrJ5qT6rk3666j+0MGkC51IY7ge8UuYWnJ2Aai2p2SjokDjnRaEclv6MHxahvqHnpjQBffym1WDIwkxuFHmhcJWMIz5WQwuPI9/heDZt+OPD91L2/Mz37pDoG6M7gD9ShaNRC3SAb4U4WZg+v/HFsKjCXTXIaJC1mCGxJTfJ0EZGS+Etsuinks0pOu30tdSY7vq2dMpuREpmRVHh3M+p+lCPhtBLULlp/rwWuBEr6llY4KcIkqICURe49pRwumxprhtzkYloXquamstGvA8FjRh7aJ86rqK3JDln0PuE2dXpaGVa8KPPtgQv3o0dwnZU0k+AfcnYLIRznvWKGD0ec53Hv47P7qc5jBuErBvHiKf8z2qpGg5jzsfK1FjGhrqYSmnml4EIMac8otmeMPPtynwMUN8ZVlEZ/K/juUB0RZGPSR4fhroLUFIqi8fmOQvYBt9to8ycSwpmCPlu/MnOPpF3u3BdaYux35n4QFU93JwLNj+xOkNzDgbHn9uAwgsj9O7Ky9E/cZQLmBX8T3pNAJOiEDh+zxFw5ItvpAL9GqlYzV3R4rN4fvJoQ7NFyHavocIH/2e2iE1kEnPMpAEEoKC2w+SpCR5rp7jj0rL6ruOxMZF57PbUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwDe91Aey5ZIoY6QgZpSDQb7EcUpirBsMb9fUyns9pqYbLlNxpUPODCLYm/w9qfTRY0hbrfnuwJ5Ksr4hBEEfhAw==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "9516FF4C8650796025AF4FC6A13FC033766F1E6F32FB203E3562DAAFB9DF0794", + "noteCommitment": { + "type": "Buffer", + "data": "base64:n7WhruSOYlHdzmXQ8623/m6JA4f4UNHJvw49tRgF6Es=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:TcR8dCjz1KafvPTpoXyaOdrfX452N0ky+VIy7+oRcpo=" + }, + "target": "9255858786337818395603165512831024101510453493377417362192396248796027", + "randomness": "0", + "timestamp": 1719441728127, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAzawMVj35WwB5iqVoxBvfa/MH8coQ59PAIidM7hSNd7u4XqrDEKQHo7DGK/UfWpRKZIFACWyWMa7NuFTY+byae5k6NPkOrXzvynGy+FRrc+Wxgiko1nc8iwYjWCv4eVATbSgW7nNzwaXFb+aKVYI6gOpAbWuIkWqo09TvSF+cG8YFRYkSSdjyxf7QACJI4Ltis/qChWSKEApBflq0SYk8M8ubbJKi6FbDl7Syq8bcnnW5aspJeOTzInfk1+xuIwRW0KJMn+ZayDuTywF/xqPOYJmu5CZrmgQPQ6mOP/pSR5ixzuggufDGyyws3gDW12saJQnM88T+BUVFBCF9nTCkFeFBPjm6DevmOtthtd42pytHNEbt9WgdLjrg4jssyJMSdV0Qp7VV+2TkIoV3kbYqEhUXrU6sZfeSfyWFcBVt4Nicr1z03qGFfZEjOzb7IKJf4PBgtk0chiUkf1NwcvaRHR7B+bIm+0tftxR+/1yaFGfJJ1crJYfr4k+ENHMFH+xHDL7Cknr1xyve9VQMMIRVmXPk3yy/lrVCilrrJGz5QCgJutiNNp5f7K4hq+7s4reQn6PlXBtjDG1+5HywBVHYpsJVSTxf05FFRRE4ESgNS76nqYS2VLz74klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw0/P2UgYHAJ8OVGWRUsY5K4szn30ehU7Phu5nG0Q98/Ijg3o+WTPBbUGhg2OaH5GAti+FCi+MENAB1IYQfQzhAw==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "1CC0F6BE54A623A0B0AC13E0A41A14ADE49A0E214B3E26F2FCCC109993F8F76F", + "noteCommitment": { + "type": "Buffer", + "data": "base64:s/TbP//NX4N1uVEg4cAjgzAsCsMBivJawUN03ssIyhY=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:AoNUIkCzQdk6lISiTioaAD6h4GcqiGLFCjPlZzPLoVU=" + }, + "target": "9228823284279306817296266184515742822248210830185427859262273659833347", + "randomness": "0", + "timestamp": 1719441728574, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA/f8lGM1diIN/TGfxQa5m4Yh5gbEYdtBrAMeFmrp52MiCrS+R9HkZvTbzqyt7zewkoA6TMjJmiuWrgWyGNfu4csVpIrTJ/CUY/FC7LHHs7qSK76JEUW0x4UrbP7fJqvUKcxHSv2POYPfFsniAd/j3/AEr947rXk/vEbfK9PonGZIBPca/Pw9AQVOioqeiPJOHabEUHXvXtFdmgCf4GZHArNuRxIPmHYNPPevamn2x/Ey4nn+iB0ouM+/meFKa+RZfrWhY6YKkX01hSR35SQLVKkXdYulQBx0fVggplXh1OKQhtTuMq7ht2jr2eY3KaiLM7JgNt97CFNJbOcS18T6Ji4/lw6Dwax+JaI1gbtO79IVl/6JdEARBZqwS+0FPYvk6forfgLp0OyJx6zeSx/jmYTMss1h7tI0Z1l7ncyzVCOx6Sg+N5E0HtAHUesnW8YxYo9kDnM7el4ogd1jIAEBOV6yuIvZ6DAgfLdHQzFJ6ph11R/JyTwSxalxRfqtJPcesd7NL8pbL+FO8/2wgv0U2JhuMqx1TAZi/AaaYtFvVoQnsFdfVi6cT0EqIqsprKDiPcCbZWnKD7/opbo3+DmHtgoUW8ipOWQPNwegIYbNtUlmqH/YI3+aTq0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw5VIIZj/q1ET+T7hlOn3tMl4l/VLHOYvcUlXqDsFU8ktlzQr1NX7qIXNV/3fZQu2u4aktXiz1/ZG/g8ZAv5jiAA==" + } + ] + }, + { + "header": { + "sequence": 5, + "previousBlockHash": "76CA8ADA5607F4B0D5B17ED4F9D47E09174EE39FBB7A3094298B2CA23D2A9745", + "noteCommitment": { + "type": "Buffer", + "data": "base64:+LliQic2PDDrTqKNNxZagq3h8/AvRKz5r4w2z+kEsWo=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:P7S39hVJs4dVJgBEECmP54qPs3Bv+07+9aV/1eqTDoA=" + }, + "target": "9201866281654531936596795386791503876274441021197252859723586932895305", + "randomness": "0", + "timestamp": 1719441729028, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 7, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAxeeweRxo6iD3UcX4YXg1XwMknBV1aVkLwNfgye3ZDcOJVsC2JE/9012EEZ2ts8MQs7/PkGwHhvNuUOxQg6+sDv+NjzvGPYPiiTn4FNBhxbCsDx2f8M6KT0arJtIslzdQPehs3OVemEILnv+YT/tfGcyfWLpRg8Z5FHirnEN6qocGgUTBt95RADnq7z7Vnixy4MbiArkO8I5fjNcfwDouZTRJwFiQO8QnHiAHg9vUJGeRPGNC/4BILyT5gHnoFoTgxl/w9RZf9ReTQ+NvBIOgueSi7Q3Ppr98HQwTxVDoW9q1yBRa2hey/DF7WSs8EEmdRvZiqeDp0pjDwnGPXpUgXQAxsu4BaasNvB8mexjSWKwudPEHmkk9tQOBIZZzgmo4LvGtt7IbeTL9Omy9CYigpHm0DPtENirBtzaQbtBny2UUcvQH0X7vslfFrMg0tfW7BR6TrEOk5P7fMhfsX942WzqWv4h9hbrTMeN4j40N/8VeOObAES7rCL88qANBNUybNDVsWVQLxRhgoS7jSKjSKqcfPrIVzAj6pjJBQclh7veuYP9UaGD5fj8jVdSKsZAJ18SYOd2y2JSGHNBkXyIq/Bbm4k2A+mWDMVXlEMaI7G6LbuGEbEfhnUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw8v24RV1PeDi9MINLvOGvzD7M0IqLrTLyK9tHcaUUWSaQjUMMRVnUeFbzIM+0P2tGnclKf8NXpbsZjdtSi65jAQ==" + } + ] + }, + { + "header": { + "sequence": 6, + "previousBlockHash": "FE9F2C2EF1A280EE9F453B561E9E0EDB9DF95CA68A8CFB61206B06C63245CD0B", + "noteCommitment": { + "type": "Buffer", + "data": "base64:+06hDLNIGHR6hgoU3FNLh2fO5Er6p5RYs5AsN0wSvQU=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:mciVbP+cWYTL43++zveilipF5aqb5Epyy8towzxjFkg=" + }, + "target": "9174987784651351638043000274530578397566067964335270621952759689537226", + "randomness": "0", + "timestamp": 1719441729496, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 8, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAICBDDjaZTzDXobtlSEQHQNuc7wbFkF5LDqtvgnEoSj64ZMHwTi2MSIM+mOeeUHalIr1lQn15FhOc+vR21CxnMATPpQTutKO5mSp1xXUDEUmYn8CHjUuEUT53aIzftuXqE3xEa3NHxmA+uf30HEgC5j/B8mnPBesNw+JeYisBd3ISJ/9boxXBUK4J/MmFv/H3KZA+JBGDteH+/AIE5WyQyjL0YAVTK0frAr20W80+sZmEHpeL87oSpUMoSp2dmTLc387TQVXgqZ+uEiGDsNeycoWGMI0oCIV/lQmeo8xj4aXxRteViJDfoU5wt/v/I95DALPR+wMPRdQiKwr9ReuCEztGVVOK535c8QimYYaKQpVW1iVZ1+aa7W76kO0cJM0s1lG8o1uT4ktFMS/XK+6pGkmO3eiAPduR+PTq6ARH75MKAjDarGTHPD8WVzttDkFWMvnB2rHtFSrL8eO3Z1jKmt7OGLHvxPvoVlYA5nKNr2j1emesyGmmVS9p4Pn1IZUnSVuemDaoF/jpbSwWyfLSVvPd97ARQkitNQS/n0rlO+7AeIlruw7gOutSduazCDIoQYJGcNGYtjToyhtRSIdTeFUhzHMK9ZMT9apQ9mmDJqS2QK9S2+Lhbklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwHCCkwG0JEW+vfgElBFXqaqC+qmQw6+8fLbv4HdV7h5c4McA0TJEe+ChIrc8h6exdmKOGOsQvUjGvvDR41zuoDA==" + } + ] + } + ] +} \ No newline at end of file diff --git a/ironfish/src/wallet/scanner/noteDecryptor.test.ts b/ironfish/src/wallet/scanner/noteDecryptor.test.ts new file mode 100644 index 0000000000..d40b2c9775 --- /dev/null +++ b/ironfish/src/wallet/scanner/noteDecryptor.test.ts @@ -0,0 +1,147 @@ +/* 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 } from '../../assert' +import { Block } from '../../primitives' +import { createNodeTest, useAccountFixture, useMinerBlockFixture } from '../../testUtilities' +import { Account } from '../../wallet' +import { BackgroundNoteDecryptor, DecryptNotesFromTransactionsCallback } from './noteDecryptor' + +describe('BackgroundNoteDecryptor', () => { + const nodeTest = createNodeTest() + + /** + * Creates a series of notes on the chain, and returns the blocks that contain such notes. + */ + const createTestNotes = async ( + spec: ReadonlyArray<[account: Account, notesCount: number]>, + ): Promise> => { + const blocks = [] + for (const [account, notesCount] of spec) { + for (let i = 0; i < notesCount; i++) { + const block = await useMinerBlockFixture( + nodeTest.chain, + undefined, + account, + nodeTest.wallet, + ) + await expect(nodeTest.chain).toAddBlock(block) + blocks.push(block) + } + } + return blocks + } + + it('decrypts notes belonging to different accounts', async () => { + const accountA = await useAccountFixture(nodeTest.wallet, 'a') + const accountB = await useAccountFixture(nodeTest.wallet, 'b') + + const blocks = await createTestNotes([ + [accountA, 5], + [accountB, 3], + [accountA, 2], + [accountB, 2], + ]) + expect(blocks.length).toBe(12) + + const decryptor = new BackgroundNoteDecryptor(nodeTest.workerPool, nodeTest.sdk.config, { + decryptForSpender: true, + }) + + decryptor.start() + + const callback = jest.fn< + ReturnType, + jest.ArgumentsOf + >() + + for (const block of blocks) { + await decryptor.decryptNotesFromBlock( + block.header, + block.transactions, + [accountA, accountB], + callback, + ) + } + + await decryptor.flush() + decryptor.stop() + + // Check that the callback was called the right number of times + expect(callback).toHaveBeenCalledTimes(2 * blocks.length) + + // Check that the correct number of notes was decrypted + const totalNotesCount = new Map() + for (const [account, _blockHeader, transactions] of callback.mock.calls) { + let notesForAccount = totalNotesCount.get(account.id) ?? 0 + notesForAccount += transactions + .map(({ decryptedNotes }) => decryptedNotes.length) + .reduce((acc, item) => acc + item, 0) + totalNotesCount.set(account.id, notesForAccount) + } + expect(totalNotesCount).toEqual( + new Map([ + [accountA.id, 7], + [accountB.id, 5], + ]), + ) + + // Check the individual callback calls + const expectedCalls = [ + { account: accountA, block: blocks[0], decrypted: true }, + { account: accountB, block: blocks[0], decrypted: false }, + { account: accountA, block: blocks[1], decrypted: true }, + { account: accountB, block: blocks[1], decrypted: false }, + { account: accountA, block: blocks[2], decrypted: true }, + { account: accountB, block: blocks[2], decrypted: false }, + { account: accountA, block: blocks[3], decrypted: true }, + { account: accountB, block: blocks[3], decrypted: false }, + { account: accountA, block: blocks[4], decrypted: true }, + { account: accountB, block: blocks[4], decrypted: false }, + + { account: accountA, block: blocks[5], decrypted: false }, + { account: accountB, block: blocks[5], decrypted: true }, + { account: accountA, block: blocks[6], decrypted: false }, + { account: accountB, block: blocks[6], decrypted: true }, + { account: accountA, block: blocks[7], decrypted: false }, + { account: accountB, block: blocks[7], decrypted: true }, + + { account: accountA, block: blocks[8], decrypted: true }, + { account: accountB, block: blocks[8], decrypted: false }, + { account: accountA, block: blocks[9], decrypted: true }, + { account: accountB, block: blocks[9], decrypted: false }, + + { account: accountA, block: blocks[10], decrypted: false }, + { account: accountB, block: blocks[10], decrypted: true }, + { account: accountA, block: blocks[11], decrypted: false }, + { account: accountB, block: blocks[11], decrypted: true }, + ] + expect(callback).toHaveBeenCalledTimes(expectedCalls.length) + + let noteIndex = nodeTest.chain.genesis.noteSize + + for (const [callIndex, { account, block, decrypted }] of expectedCalls.entries()) { + const transactions = block.transactions.map((transaction) => { + const decryptedNotes = [] + if (decrypted) { + Assert.isNotNull(noteIndex) + decryptedNotes.push({ + index: noteIndex, + forSpender: false, + hash: expect.anything(), + nullifier: expect.anything(), + serializedNote: expect.anything(), + }) + noteIndex += 1 + } + return { transaction, decryptedNotes } + }) + expect(callback).toHaveBeenNthCalledWith( + callIndex + 1, + account, + block.header, + transactions, + ) + } + }) +}) diff --git a/ironfish/src/wallet/scanner/noteDecryptor.ts b/ironfish/src/wallet/scanner/noteDecryptor.ts new file mode 100644 index 0000000000..3c80aed5dc --- /dev/null +++ b/ironfish/src/wallet/scanner/noteDecryptor.ts @@ -0,0 +1,221 @@ +/* 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 } from '../../assert' +import { Config } from '../../fileStores' +import { BlockHeader } from '../../primitives' +import { Transaction } from '../../primitives/transaction' +import { AsyncQueue } from '../../utils/asyncQueue' +import { WorkerPool } from '../../workerPool' +import { Job } from '../../workerPool/job' +import { + DecryptedNote, + DecryptNotesOptions, + DecryptNotesRequest, + DecryptNotesResponse, +} from '../../workerPool/tasks/decryptNotes' +import { JobAbortedError } from '../../workerPool/tasks/jobAbort' +import { Account } from '../account/account' + +export type DecryptNotesFromTransactionsCallback = ( + account: Account, + blockHeader: BlockHeader, + transactions: Array<{ transaction: Transaction; decryptedNotes: Array }>, +) => Promise + +export class BackgroundNoteDecryptor { + private isStarted = false + + private triggerFlushed: (() => void) | null = null + private triggerStopped: (() => void) | null = null + + private onFlushed: Promise = Promise.resolve() + private onStopped: Promise = Promise.resolve() + + private readonly workerPool: WorkerPool + private readonly options: DecryptNotesOptions + private readonly decryptQueue: AsyncQueue<{ + job: Job + accounts: ReadonlyArray + blockHeader: BlockHeader + transactions: ReadonlyArray + callback: DecryptNotesFromTransactionsCallback + }> + + constructor(workerPool: WorkerPool, config: Config, options: DecryptNotesOptions) { + this.workerPool = workerPool + this.options = options + + let queueSize = 8 * workerPool.numWorkers + const maxQueueSize = config.get('walletSyncingMaxConcurrency') + if (maxQueueSize > 0) { + queueSize = Math.min(queueSize, maxQueueSize) + } + queueSize = Math.max(queueSize, 1) + this.decryptQueue = new AsyncQueue(queueSize) + } + + start(abort?: AbortController) { + if (!this.isStarted) { + this.isStarted = true + this.onStopped = new Promise((resolve) => (this.triggerStopped = resolve)) + void this.decryptLoop() + + if (abort) { + abort.signal.addEventListener('abort', this.stop.bind(this)) + } + } + } + + stop() { + if (this.isStarted) { + this.isStarted = false + for (const { job } of this.decryptQueue) { + job.abort() + } + this.decryptQueue.clear() + if (this.triggerStopped) { + this.triggerStopped() + } + } + } + + private async decryptLoop(): Promise { + while (this.isStarted) { + if (this.decryptQueue.isEmpty() && this.triggerFlushed) { + this.triggerFlushed() + this.triggerFlushed = null + } + + const item = await Promise.race([this.decryptQueue.pop(), this.onStopped]) + if (!item) { + break + } + + const { job, accounts, blockHeader, transactions, callback } = item + + let decryptNotesResponse + try { + decryptNotesResponse = await job.result() + } catch (e) { + if (e instanceof JobAbortedError) { + break + } + throw e + } + + if (!this.isStarted) { + break + } + + Assert.isInstanceOf(decryptNotesResponse, DecryptNotesResponse) + const decryptedNotes = decryptNotesResponse.mapToAccounts( + accounts.map((account) => ({ accountId: account.id })), + ) + + for (const { account, decryptedTransactions } of regroupNotes( + accounts, + transactions, + decryptedNotes, + )) { + if (!this.isStarted) { + break + } + await callback(account, blockHeader, decryptedTransactions) + } + } + } + + /** + * Waits for all the in flight decrypt requests to be fully processed. + */ + async flush(): Promise { + if (!this.isStarted) { + return + } + await this.onFlushed + } + + decryptNotesFromBlock( + blockHeader: BlockHeader, + transactions: ReadonlyArray, + accounts: ReadonlyArray, + callback: DecryptNotesFromTransactionsCallback, + ): Promise { + if (!this.isStarted) { + throw new Error('decryptor was not started') + } + + if (!this.triggerFlushed) { + this.onFlushed = new Promise((resolve) => (this.triggerFlushed = resolve)) + } + + const accountKeys = accounts.map((account) => ({ + incomingViewKey: account.incomingViewKey, + outgoingViewKey: account.outgoingViewKey, + viewKey: account.viewKey, + })) + Assert.isNotNull(blockHeader.noteSize) + + const encryptedNotes = [] + let currentNoteIndex = transactions + .map((transaction) => transaction.notes.length) + .reduce((accumulator, numNotes) => accumulator - numNotes, blockHeader.noteSize) + + for (const transaction of transactions) { + for (const note of transaction.notes) { + encryptedNotes.push({ serializedNote: note.serialize(), currentNoteIndex }) + currentNoteIndex++ + } + } + + const decryptNotesRequest = new DecryptNotesRequest( + accountKeys, + encryptedNotes, + this.options, + ) + const job = this.workerPool.execute(decryptNotesRequest) + + return this.decryptQueue.push({ + job, + accounts, + blockHeader, + transactions, + callback, + }) + } +} + +/** + * Reassociates each decrypted note to its corresponding transaction. + */ +function* regroupNotes( + accounts: ReadonlyArray, + transactions: ReadonlyArray, + decryptedNotes: ReadonlyMap>, +): Generator<{ + account: Account + decryptedTransactions: Array<{ + transaction: Transaction + decryptedNotes: Array + }> +}> { + for (const account of accounts) { + let notesOffset = 0 + const flatNotes: ReadonlyArray = decryptedNotes.get(account.id) ?? [] + const groupedNotes: Array<{ + transaction: Transaction + decryptedNotes: Array + }> = [] + + for (const transaction of transactions) { + const decryptedNotes = flatNotes + .slice(notesOffset, notesOffset + transaction.notes.length) + .filter((note) => note !== null) as Array + groupedNotes.push({ transaction, decryptedNotes }) + notesOffset += transaction.notes.length + } + + yield { account, decryptedTransactions: groupedNotes } + } +} diff --git a/ironfish/src/wallet/scanner/walletScanner.test.ts b/ironfish/src/wallet/scanner/walletScanner.test.ts new file mode 100644 index 0000000000..9a039160fb --- /dev/null +++ b/ironfish/src/wallet/scanner/walletScanner.test.ts @@ -0,0 +1,472 @@ +/* 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 } from '../../assert' +import { Blockchain } from '../../blockchain' +import { Block, BlockHeader } from '../../primitives' +import { createNodeTest, useAccountFixture, useMinerBlockFixture } from '../../testUtilities' +import { AsyncUtils } from '../../utils' +import { Account, Wallet } from '../../wallet' +import { BackgroundNoteDecryptor } from './noteDecryptor' + +describe('WalletScanner', () => { + const nodeTest = createNodeTest() + + beforeEach(() => { + jest.restoreAllMocks() + }) + + /** + * Creates a series of notes on the chain, and returns the blocks that contain such notes. + */ + const createTestNotes = async ( + chain: Blockchain, + wallet: Wallet, + spec: ReadonlyArray<[account: Account, notesCount: number]>, + ): Promise> => { + const blocks = [] + for (const [account, notesCount] of spec) { + for (let i = 0; i < notesCount; i++) { + const block = await useMinerBlockFixture(chain, undefined, account, wallet) + await expect(chain).toAddBlock(block) + blocks.push(block) + } + } + return blocks + } + + it('adds transactions to the wallet db with decrypted notes', async () => { + const connectBlockForAccount = jest.spyOn(nodeTest.wallet, 'connectBlockForAccount') + + const account = await useAccountFixture(nodeTest.wallet, 'a') + const blocks = await createTestNotes(nodeTest.chain, nodeTest.wallet, [[account, 3]]) + + expect(connectBlockForAccount).not.toHaveBeenCalled() + await nodeTest.wallet.scan() + expect(connectBlockForAccount).toHaveBeenCalledTimes(3) + + const initialNoteIndex = nodeTest.chain.genesis.noteSize + Assert.isNotNull(initialNoteIndex) + + for (const [i, block] of blocks.entries()) { + expect(block.transactions.length).toBe(1) + + expect(connectBlockForAccount).toHaveBeenNthCalledWith( + i + 1, + account, + block.header, + block.transactions.map((transaction) => ({ + transaction, + decryptedNotes: [ + expect.objectContaining({ + index: i + initialNoteIndex, + forSpender: false, + hash: expect.anything(), + nullifier: expect.anything(), + serializedNote: expect.anything(), + }), + ], + })), + true, + ) + + for (const transaction of block.transactions) { + const storedTransaction = await account.getTransaction(transaction.hash()) + expect(storedTransaction).toBeDefined() + expect(storedTransaction?.blockHash).toEqual(block.header.hash) + expect(storedTransaction?.sequence).toEqual(block.header.sequence) + } + } + + const allStoredTransactions = await AsyncUtils.materialize(account.getTransactions()) + expect(allStoredTransactions.length).toBe(3) + + await expect(account.getBalance(Asset.nativeId(), 0)).resolves.toMatchObject({ + confirmed: 3n * 2000000000n, + unconfirmed: 3n * 2000000000n, + }) + }) + + it('updates the account head hash', async () => { + const account = await useAccountFixture(nodeTest.wallet, 'a') + const blocks = await createTestNotes(nodeTest.chain, nodeTest.wallet, [[account, 3]]) + + await nodeTest.wallet.scan() + + const accountHead = await account.getHead() + expect(accountHead?.hash).toEqualHash(blocks[2].header.hash) + }) + + it('ignores accounts that have scanning disabled', async () => { + const connectBlockForAccount = jest.spyOn(nodeTest.wallet, 'connectBlockForAccount') + + const accountA = await useAccountFixture(nodeTest.wallet, 'a') + const accountB = await useAccountFixture(nodeTest.wallet, 'b') + const accountC = await useAccountFixture(nodeTest.wallet, 'c') + const accountD = await useAccountFixture(nodeTest.wallet, 'd') + + await accountB.updateScanningEnabled(false) + await accountD.updateScanningEnabled(false) + + const blocks = await createTestNotes(nodeTest.chain, nodeTest.wallet, [ + [accountA, 1], + [accountB, 1], + [accountC, 1], + [accountD, 1], + [accountD, 1], + [accountB, 1], + [accountA, 1], + [accountC, 1], + ]) + + expect(connectBlockForAccount).not.toHaveBeenCalled() + await nodeTest.wallet.scan() + expect(connectBlockForAccount).toHaveBeenCalledTimes(16) + + const initialNoteIndex = nodeTest.chain.genesis.noteSize + Assert.isNotNull(initialNoteIndex) + + const expectedConnectedAccountBlocks = [ + { account: accountA, block: blocks[0], decryptedNoteIndexes: [initialNoteIndex] }, + { account: accountC, block: blocks[0], decryptedNoteIndexes: [] }, + { account: accountA, block: blocks[1], decryptedNoteIndexes: [] }, + { account: accountC, block: blocks[1], decryptedNoteIndexes: [] }, + { account: accountA, block: blocks[2], decryptedNoteIndexes: [] }, + { account: accountC, block: blocks[2], decryptedNoteIndexes: [initialNoteIndex + 2] }, + { account: accountA, block: blocks[3], decryptedNoteIndexes: [] }, + { account: accountC, block: blocks[3], decryptedNoteIndexes: [] }, + { account: accountA, block: blocks[4], decryptedNoteIndexes: [] }, + { account: accountC, block: blocks[4], decryptedNoteIndexes: [] }, + { account: accountA, block: blocks[5], decryptedNoteIndexes: [] }, + { account: accountC, block: blocks[5], decryptedNoteIndexes: [] }, + { account: accountA, block: blocks[6], decryptedNoteIndexes: [initialNoteIndex + 6] }, + { account: accountC, block: blocks[6], decryptedNoteIndexes: [] }, + { account: accountA, block: blocks[7], decryptedNoteIndexes: [] }, + { account: accountC, block: blocks[7], decryptedNoteIndexes: [initialNoteIndex + 7] }, + ] + + for (const [ + i, + { account, block, decryptedNoteIndexes }, + ] of expectedConnectedAccountBlocks.entries()) { + expect(block.transactions.length).toBe(1) + + expect(connectBlockForAccount).toHaveBeenNthCalledWith( + i + 1, + account, + block.header, + block.transactions.map((transaction) => ({ + transaction, + decryptedNotes: decryptedNoteIndexes.map( + (index) => + expect.objectContaining({ + index, + forSpender: false, + hash: expect.anything(), + nullifier: expect.anything(), + serializedNote: expect.anything(), + }) as unknown, + ), + })), + true, + ) + + for (const transaction of block.transactions) { + const storedTransaction = await account.getTransaction(transaction.hash()) + if (!decryptedNoteIndexes.length) { + expect(storedTransaction).not.toBeDefined() + } else { + expect(storedTransaction).toBeDefined() + expect(storedTransaction?.blockHash).toEqual(block.header.hash) + expect(storedTransaction?.sequence).toEqual(block.header.sequence) + } + } + } + + await expect(nodeTest.wallet.getBalance(accountA, Asset.nativeId())).resolves.toMatchObject( + { + confirmed: 2n * 2000000000n, + unconfirmed: 2n * 2000000000n, + }, + ) + await expect(nodeTest.wallet.getBalance(accountB, Asset.nativeId())).resolves.toMatchObject( + { + confirmed: 0n, + unconfirmed: 0n, + }, + ) + await expect(nodeTest.wallet.getBalance(accountC, Asset.nativeId())).resolves.toMatchObject( + { + confirmed: 2n * 2000000000n, + unconfirmed: 2n * 2000000000n, + }, + ) + await expect(nodeTest.wallet.getBalance(accountD, Asset.nativeId())).resolves.toMatchObject( + { + confirmed: 0n, + unconfirmed: 0n, + }, + ) + + expect((await accountA.getHead())?.hash).toEqualHash(blocks[7].header.hash) + expect((await accountB.getHead())?.hash).toEqualHash(nodeTest.chain.genesis.hash) + expect((await accountC.getHead())?.hash).toEqualHash(blocks[7].header.hash) + expect((await accountD.getHead())?.hash).toEqualHash(nodeTest.chain.genesis.hash) + }) + + it('skips decryption for accounts with createdAt later than the block header', async () => { + const accountA = await useAccountFixture(nodeTest.wallet, 'a') + expect(accountA.createdAt?.hash).toEqualHash(nodeTest.chain.genesis.hash) + + const firstBlocks = await createTestNotes(nodeTest.chain, nodeTest.wallet, [[accountA, 3]]) + + const accountB = await useAccountFixture(nodeTest.wallet, 'b') + expect(accountB.createdAt?.hash).toEqualHash(firstBlocks[2].header.hash) + + const lastBlocks = await createTestNotes(nodeTest.chain, nodeTest.wallet, [[accountB, 3]]) + + const decryptNotesFromBlock = jest.spyOn( + BackgroundNoteDecryptor.prototype, + 'decryptNotesFromBlock', + ) + + await nodeTest.wallet.reset() + await nodeTest.wallet.scan() + + const genesisBlock = await nodeTest.chain.getBlock(nodeTest.chain.genesis) + Assert.isNotNull(genesisBlock) + + const blocks = [genesisBlock, ...firstBlocks, ...lastBlocks] + + expect(decryptNotesFromBlock).toHaveBeenCalledTimes(blocks.length) + + for (const [i, block] of blocks.slice(1, 3).entries()) { + expect(decryptNotesFromBlock).toHaveBeenNthCalledWith( + i + 2, + block.header, + block.transactions, + [expect.objectContaining({ incomingViewKey: accountA.incomingViewKey })], + expect.anything(), + ) + } + for (const [i, block] of blocks.slice(3).entries()) { + expect(decryptNotesFromBlock).toHaveBeenNthCalledWith( + i + 4, + block.header, + block.transactions, + [ + expect.objectContaining({ incomingViewKey: accountA.incomingViewKey }), + expect.objectContaining({ name: accountB.name }), + ], + expect.anything(), + ) + } + }) + + it('skips blocks preceeding the lowest createdAt', async () => { + const decryptNotesFromBlock = jest.spyOn( + BackgroundNoteDecryptor.prototype, + 'decryptNotesFromBlock', + ) + const connectBlockForAccount = jest.spyOn(nodeTest.wallet, 'connectBlockForAccount') + + const accountA = await useAccountFixture(nodeTest.wallet, 'a') + const firstBlocks = await createTestNotes(nodeTest.chain, nodeTest.wallet, [[accountA, 3]]) + await nodeTest.wallet.removeAccount(accountA) + + const accountB = await useAccountFixture(nodeTest.wallet, 'b') + const lastBlocks = await createTestNotes(nodeTest.chain, nodeTest.wallet, [[accountB, 3]]) + + await nodeTest.wallet.reset() + await nodeTest.wallet.scan() + + const blocks = [firstBlocks[2], ...lastBlocks] + + expect(decryptNotesFromBlock).toHaveBeenCalledTimes(blocks.length) + expect(connectBlockForAccount).toHaveBeenCalledTimes(blocks.length) + + for (const [i, block] of blocks.entries()) { + expect(decryptNotesFromBlock).toHaveBeenNthCalledWith( + i + 1, + block.header, + block.transactions, + [expect.objectContaining({ incomingViewKey: accountB.incomingViewKey })], + expect.anything(), + ) + expect(connectBlockForAccount).toHaveBeenNthCalledWith( + i + 1, + expect.objectContaining({ incomingViewKey: accountB.incomingViewKey }), + block.header, + expect.anything(), + true, + ) + } + }) + + describe('restarts scanning', () => { + // Set up the BackgroundNoteDecryptor so that we can pause and resume the + // scan after each block that gets processed. + let continueScan: () => void = () => {} + let notifyDecryptCall: ((blockHeader: BlockHeader) => void) | null = null + let decryptCallPromise: Promise | null = null + + const nextDecryptCall = async (): Promise => { + Assert.isNotNull(decryptCallPromise) + const blockHeader = await decryptCallPromise + decryptCallPromise = new Promise((resolve) => { + notifyDecryptCall = resolve + }) + return blockHeader + } + + const patchWalletScanner = (wallet: Wallet) => { + const connectBlockOrig = wallet.scanner.connectBlock.bind(wallet.scanner) + jest + .spyOn(wallet.scanner, 'connectBlock') + .mockImplementation(async (blockHeader, ...args) => { + Assert.isNotNull(notifyDecryptCall) + notifyDecryptCall(blockHeader) + void connectBlockOrig(blockHeader, ...args) + return new Promise((resolve) => { + continueScan = resolve + }) + }) + + decryptCallPromise = new Promise((resolve) => { + notifyDecryptCall = resolve + }) + } + + it('when accounts are imported', async () => { + const { chain, wallet } = await nodeTest.createSetup({ + config: { walletSyncingMaxQueueSize: 1 }, + }) + patchWalletScanner(wallet) + + // Create 2 accounts, and remove the first one (so that we can re-import it later) + const genesisBlock = await chain.getBlock(chain.genesis) + Assert.isNotNull(genesisBlock) + const blocks = [genesisBlock] + const accountA = await useAccountFixture(wallet, 'a') + blocks.push(...(await createTestNotes(chain, wallet, [[accountA, 5]]))) + const accountB = await useAccountFixture(wallet, 'b') + blocks.push(...(await createTestNotes(chain, wallet, [[accountB, 5]]))) + await wallet.removeAccount(accountA) + + expect(blocks.length).toBe(11) + + // Start scanning + await wallet.reset() + const scanPromise = wallet.scan() + + // Check that the scanning begins at the `accountB` creation block (and scan a few blocks) + for (let i = 5; i <= 8; i++) { + continueScan() + const firstBlockHeader = await nextDecryptCall() + expect(firstBlockHeader.hash).toEqualHash(blocks[i].header.hash) + } + + // Now import `accountA` + await wallet.importAccount(accountA.serialize()) + + // Check that scanning resumes at the `accountA` creation block (and scan till the end) + for (let i = 0; i <= 10; i++) { + continueScan() + const blockHeader = await nextDecryptCall() + expect(blockHeader.hash).toEqualHash(blocks[i].header.hash) + } + + // Scan should be done + continueScan() + await scanPromise + }) + + it('when accounts are deleted', async () => { + const { chain, wallet } = await nodeTest.createSetup({ + config: { walletSyncingMaxQueueSize: 1 }, + }) + patchWalletScanner(wallet) + + // Create 2 accounts + const genesisBlock = await chain.getBlock(chain.genesis) + Assert.isNotNull(genesisBlock) + const blocks = [genesisBlock] + const accountA = await useAccountFixture(wallet, 'a') + blocks.push(...(await createTestNotes(chain, wallet, [[accountA, 5]]))) + const accountB = await useAccountFixture(wallet, 'b') + blocks.push(...(await createTestNotes(chain, wallet, [[accountB, 5]]))) + + expect(blocks.length).toBe(11) + + // Start scanning + await wallet.reset() + const scanPromise = wallet.scan() + + // Check that the scanning begins at the `accountA` creation block (and scan a few blocks) + for (let i = 0; i <= 2; i++) { + continueScan() + const blockHeader = await nextDecryptCall() + expect(blockHeader.hash).toEqualHash(blocks[i].header.hash) + } + + // Now delete `accountA` + // + // (Need to use `removeAccountByName` instead of `removeAccount` because + // the previous call to `wallet.reset()` has caused the account id to + // change) + await wallet.removeAccountByName(accountA.name) + + // Check that scanning skips blocks and resumes at the `accountB` creation block (and scan till the end) + for (let i = 5; i <= 10; i++) { + continueScan() + const blockHeader = await nextDecryptCall() + expect(blockHeader.hash).toEqualHash(blocks[i].header.hash) + } + + // Scan should be done + continueScan() + await scanPromise + }) + + it('when accounts are reset', async () => { + const { chain, wallet } = await nodeTest.createSetup({ + config: { walletSyncingMaxQueueSize: 1 }, + }) + patchWalletScanner(wallet) + + const genesisBlock = await chain.getBlock(chain.genesis) + Assert.isNotNull(genesisBlock) + const blocks = [genesisBlock] + const account = await useAccountFixture(wallet, 'a') + blocks.push(...(await createTestNotes(chain, wallet, [[account, 5]]))) + + expect(blocks.length).toBe(6) + + // Start scanning + await wallet.reset() + const scanPromise = wallet.scan() + + // Check that the scanning begins at the genesis block (and scan a few blocks) + for (let i = 0; i <= 3; i++) { + continueScan() + const blockHeader = await nextDecryptCall() + expect(blockHeader.hash).toEqualHash(blocks[i].header.hash) + } + + // Reset the wallet + await wallet.reset() + + // Check that scanning restarts from the genesis block (and scan till the end) + for (let i = 0; i <= 5; i++) { + continueScan() + const blockHeader = await nextDecryptCall() + expect(blockHeader.hash).toEqualHash(blocks[i].header.hash) + } + + // Scan should be done + continueScan() + await scanPromise + }) + }) +}) diff --git a/ironfish/src/wallet/scanner/walletScanner.ts b/ironfish/src/wallet/scanner/walletScanner.ts index a679adbb09..5fcd894d59 100644 --- a/ironfish/src/wallet/scanner/walletScanner.ts +++ b/ironfish/src/wallet/scanner/walletScanner.ts @@ -3,23 +3,24 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import type { Blockchain } from '../../blockchain' import type { RpcClient } from '../../rpc' +import type { Account } from '../account/account' import type { Wallet } from '../wallet' -import { BufferMap } from 'buffer-map' -import { Assert } from '../../assert' +import type { HeadValue } from '../walletdb/headValue' import { Config } from '../../fileStores' import { Logger } from '../../logger' import { Mutex } from '../../mutex' import { BlockHeader, Transaction } from '../../primitives' import { AsyncUtils, BufferUtils, HashUtils } from '../../utils' -import { DecryptedNote } from '../../workerPool/tasks/decryptNotes' -import { HeadValue } from '../walletdb/headValue' +import { WorkerPool } from '../../workerPool' import { ChainProcessorWithTransactions } from './chainProcessorWithTransactions' +import { BackgroundNoteDecryptor } from './noteDecryptor' import { RemoteChainProcessor } from './remoteChainProcessor' import { ScanState } from './scanState' export class WalletScanner { readonly logger: Logger readonly wallet: Wallet + readonly workerPool: WorkerPool readonly config: Config readonly chain: Blockchain | null = null @@ -28,15 +29,23 @@ export class WalletScanner { state: ScanState | null = null lock: Mutex = new Mutex() + /** + * A snapshot of the accounts that have `scanningEnabled` set to true. Used + * to tell what accounts should be scanned, and from what block. + */ + private scanningAccounts = new Array<{ account: Account; scanFrom: HeadValue | null }>() + constructor(options: { logger: Logger wallet: Wallet + workerPool: WorkerPool config: Config nodeClient?: RpcClient | null chain?: Blockchain | null }) { this.logger = options.logger this.wallet = options.wallet + this.workerPool = options.workerPool this.config = options.config this.chain = options.chain ?? null this.nodeClient = options.nodeClient ?? null @@ -66,13 +75,19 @@ export class WalletScanner { } try { - const start = await this.wallet.getEarliestHead() + await this.refreshScanningAccounts() + + const start = this.getEarliestHead() const end = await this.wallet.getChainHead() + const decryptor = new BackgroundNoteDecryptor(this.workerPool, this.config, { + decryptForSpender: false, + }) + const chainProcessor = this.getChainProcessor(start) chainProcessor.onAdd.on(async ({ header, transactions }) => { - await this.connectBlock(header, transactions, this.state?.abortController) + await this.connectBlock(header, transactions, decryptor, this.state?.abortController) this.state?.signal(header) }) @@ -93,15 +108,31 @@ export class WalletScanner { `Scan starting from block ${scan.start.sequence} to ${scan.start.sequence}`, ) + decryptor.start(scan.abortController) + void (async () => { let hashChanged = true + while (hashChanged) { - const head = await this.wallet.getEarliestHead() - chainProcessor.hash = head?.hash ?? null + if (scan.abortController.signal.aborted) { + return + } + + if (this.haveWalletAccountsChanged()) { + // Accounts have changed in the wallet. Wait for all pending + // decrypt requests to be completed, then update the head of the + // chain processor. + await decryptor.flush() + await this.refreshScanningAccounts() + const head = this.getEarliestHead() + chainProcessor.hash = head?.hash ?? null + } const result = await chainProcessor.update({ signal: scan.abortController.signal }) hashChanged = result.hashChanged } + + await decryptor.flush() })() .then(() => { this.logger.debug( @@ -111,6 +142,8 @@ export class WalletScanner { ) }) .finally(() => { + decryptor.stop() + if (this.state === scan) { this.state = null } @@ -127,6 +160,7 @@ export class WalletScanner { async connectBlock( blockHeader: BlockHeader, transactions: Transaction[], + decryptor: BackgroundNoteDecryptor, abort?: AbortController, ): Promise { if (blockHeader.sequence % 100 === 0) { @@ -137,71 +171,52 @@ export class WalletScanner { ) } - const accounts = await AsyncUtils.filter(this.wallet.listAccounts(), async (account) => { - if (!account.scanningEnabled) { - return false - } - - const accountHead = await account.getHead() - - if (!accountHead) { - return blockHeader.sequence === 1 - } else { - return BufferUtils.equalsNullable(accountHead.hash, blockHeader.previousBlockHash) - } - }) - - const shouldDecryptAccounts = accounts.filter((a) => - this.wallet.shouldDecryptForAccount(blockHeader, a), - ) - - const shouldDecryptAccountIds = new Set(shouldDecryptAccounts.map((a) => a.id)) - - const decryptedTransactions = await Promise.all( - getTransactionsWithNoteIndex(blockHeader, transactions).map( - ({ transaction, initialNoteIndex }) => - this.wallet - .decryptNotes(transaction, initialNoteIndex, false, shouldDecryptAccounts) - .then((r) => ({ - result: r, - transaction, - })), - ), - ) - - // account id -> transaction hash -> Array - const decryptedNotesMap: Map>> = new Map() - for (const { transaction, result } of decryptedTransactions) { - for (const [accountId, decryptedNotes] of result) { - const accountTxnsMap = - decryptedNotesMap.get(accountId) ?? new BufferMap>() - accountTxnsMap.set(transaction.hash(), decryptedNotes) - decryptedNotesMap.set(accountId, accountTxnsMap) + const connectOnlyAccounts = new Array() + const decryptAndConnectAccounts = new Array() + + for (const candidate of this.scanningAccounts) { + if ( + !candidate.scanFrom || + BufferUtils.equalsNullable(candidate.scanFrom.hash, blockHeader.previousBlockHash) + ) { + candidate.scanFrom = null + + if ( + candidate.account.createdAt === null || + blockHeader.sequence >= candidate.account.createdAt.sequence + ) { + decryptAndConnectAccounts.push(candidate.account) + } else { + connectOnlyAccounts.push(candidate.account) + } } } - for (const account of accounts) { + for (const account of connectOnlyAccounts) { if (abort?.signal.aborted) { return } + await this.wallet.connectBlockForAccount(account, blockHeader, [], false) + } - const accountTxnsMap = decryptedNotesMap.get(account.id) - - const txns = transactions.map((transaction) => ({ - transaction, - decryptedNotes: accountTxnsMap?.get(transaction.hash()) ?? [], - })) - - await this.wallet.connectBlockForAccount( - account, - blockHeader, - txns, - shouldDecryptAccountIds.has(account.id), - ) + if (abort?.signal.aborted) { + return } + + return decryptor.decryptNotesFromBlock( + blockHeader, + transactions, + decryptAndConnectAccounts, + async (account, blockHeader, transactions) => { + if (abort?.signal.aborted) { + return + } + await this.wallet.connectBlockForAccount(account, blockHeader, transactions, true) + }, + ) } - async disconnectBlock( + private async disconnectBlock( header: BlockHeader, transactions: Transaction[], abort?: AbortController, @@ -252,21 +267,48 @@ export class WalletScanner { throw new Error('WalletScanner requires either chain or client') } -} - -function getTransactionsWithNoteIndex( - header: BlockHeader, - transactions: Transaction[], -): Array<{ transaction: Transaction; initialNoteIndex: number }> { - Assert.isNotNull(header.noteSize) - let initialNoteIndex = header.noteSize - const result = [] + /** + * Checks whether `scanningAccounts` is stale or up-to-date. + */ + private haveWalletAccountsChanged(): boolean { + const accountIds = new Set( + this.wallet + .listAccounts() + .filter((account) => account.scanningEnabled) + .map((account) => account.id), + ) + return ( + this.scanningAccounts.length !== accountIds.size || + !this.scanningAccounts.every(({ account }) => accountIds.has(account.id)) + ) + } - for (const transaction of transactions.slice().reverse()) { - initialNoteIndex -= transaction.notes.length - result.push({ transaction, initialNoteIndex }) + /** + * Replaces `scanningAccounts` with fresh values from the wallet. + */ + private async refreshScanningAccounts(): Promise { + this.scanningAccounts = await Promise.all( + this.wallet + .listAccounts() + .filter((account) => account.scanningEnabled) + .map(async (account) => ({ + account, + scanFrom: await account.getHead(), + })), + ) } - return result.slice().reverse() + private getEarliestHead(): HeadValue | null { + let earliestHead = null + for (const { scanFrom: head } of this.scanningAccounts) { + if (!head) { + return null + } + if (!earliestHead || earliestHead.sequence > head.sequence) { + earliestHead = head + } + } + return earliestHead + } } diff --git a/ironfish/src/wallet/wallet.test.ts b/ironfish/src/wallet/wallet.test.ts index a09906db41..86079dcd63 100644 --- a/ironfish/src/wallet/wallet.test.ts +++ b/ironfish/src/wallet/wallet.test.ts @@ -284,155 +284,6 @@ describe('Wallet', () => { }) }) - describe('scan', () => { - it('should update head status', async () => { - // G -> 1 -> 2 - const { node } = nodeTest - - const accountA = await useAccountFixture(node.wallet, 'accountA') - - const block1 = await useMinerBlockFixture(node.chain, 2, accountA) - await expect(node.chain).toAddBlock(block1) - await node.wallet.scan() - - // create a second account and import it so that its head hash is null - const { node: nodeB } = await nodeTest.createSetup() - const toImport = await useAccountFixture(nodeB.wallet, 'accountB') - - const accountB = await node.wallet.importAccount(toImport) - - // Confirm pre-rescan state - await expect(accountA.getHead()).resolves.toEqual({ - hash: block1.header.hash, - sequence: block1.header.sequence, - }) - await expect(accountB.getHead()).resolves.toEqual(null) - - // Add second block - const block2 = await useMinerBlockFixture(node.chain, 3, accountA) - await expect(node.chain).toAddBlock(block2) - - await node.wallet.scan() - - await expect(accountA.getHead()).resolves.toEqual({ - hash: block2.header.hash, - sequence: block2.header.sequence, - }) - await expect(accountB.getHead()).resolves.toEqual({ - hash: block2.header.hash, - sequence: block2.header.sequence, - }) - }) - - it('should not scan if wallet is disabled', async () => { - const { wallet, chain } = await nodeTest.createSetup({ config: { enableWallet: false } }) - - // Create a new account but don't give it an account birthday so the wallet head does not update - await useAccountFixture(wallet, 'test', { createdAt: null }) - - const block1 = await useMinerBlockFixture(chain) - await expect(chain).toAddBlock(block1) - - const scanSpy = jest.spyOn(wallet.scanner, 'scan') - await wallet.scan() - expect(scanSpy).not.toHaveBeenCalled() - }) - - it('should not scan if all accounts are up to date', async () => { - const { chain, wallet } = nodeTest - - const accountA = await useAccountFixture(wallet, 'accountA') - const accountB = await useAccountFixture(wallet, 'accountB') - - const block1 = await useMinerBlockFixture(chain) - await expect(chain).toAddBlock(block1) - - await wallet.scan() - - await expect(accountA.getHead()).resolves.toEqual({ - hash: block1.header.hash, - sequence: block1.header.sequence, - }) - await expect(accountB.getHead()).resolves.toEqual({ - hash: block1.header.hash, - sequence: block1.header.sequence, - }) - - const connectSpy = jest.spyOn(wallet, 'connectBlockForAccount') - - await wallet.scan() - - expect(connectSpy).not.toHaveBeenCalled() - }) - - it('should scan until the chain head', async () => { - const { node } = await nodeTest.createSetup() - - // create an account so that the wallet will sync - await useAccountFixture(node.wallet, 'a') - - // update wallet to genesis block - await node.wallet.scan() - - const block2 = await useMinerBlockFixture(node.chain, undefined) - await expect(node.chain).toAddBlock(block2) - const block3 = await useMinerBlockFixture(node.chain, undefined) - await expect(node.chain).toAddBlock(block3) - - expect(node.chain.head.hash).toEqualHash(block3.header.hash) - - let head = await node.wallet.getEarliestHead() - expect(head?.hash).toEqualHash(node.chain.genesis.hash) - - // set max syncing queue to 1 so that wallet only fetches one block at a time - node.wallet.scanner.config.set('walletSyncingMaxQueueSize', 1) - - await node.wallet.scan() - - head = await node.wallet.getEarliestHead() - expect(head?.hash).toEqualHash(node.chain.head.hash) - }) - - it('should start from null account head', async () => { - // G -> 1 -> 2 - const { node } = nodeTest - - const accountA = await useAccountFixture(node.wallet, 'accountA') - - const block1 = await useMinerBlockFixture(node.chain, 2, accountA) - await expect(node.chain).toAddBlock(block1) - await node.wallet.scan() - - // create a second account and import it so that its head hash is null - const { node: nodeB } = await nodeTest.createSetup() - const toImport = await useAccountFixture(nodeB.wallet, 'accountB') - - const accountB = await node.wallet.importAccount(toImport) - - // Confirm pre-rescan state - await expect(accountA.getHead()).resolves.toEqual({ - hash: block1.header.hash, - sequence: block1.header.sequence, - }) - await expect(accountB.getHead()).resolves.toEqual(null) - - // Add second block - const block2 = await useMinerBlockFixture(node.chain, 3, accountA) - await expect(node.chain).toAddBlock(block2) - - await node.wallet.scan() - - await expect(accountA.getHead()).resolves.toEqual({ - hash: block2.header.hash, - sequence: block2.header.sequence, - }) - await expect(accountB.getHead()).resolves.toEqual({ - hash: block2.header.hash, - sequence: block2.header.sequence, - }) - }) - }) - describe('getBalances', () => { it('returns balances for all unspent notes across assets for an account', async () => { const { node } = await nodeTest.createSetup() @@ -1617,181 +1468,152 @@ describe('Wallet', () => { }) }) - describe('connectBlock', () => { - it('should add transactions to the walletDb with blockHash and sequence set', async () => { - const { node } = await nodeTest.createSetup() + describe('scan', () => { + it('should update head status', async () => { + // G -> 1 -> 2 + const { node } = nodeTest - const accountA = await useAccountFixture(node.wallet, 'a') - const accountB = await useAccountFixture(node.wallet, 'b') + const accountA = await useAccountFixture(node.wallet, 'accountA') - const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA1) + const block1 = await useMinerBlockFixture(node.chain, 2, accountA) + await expect(node.chain).toAddBlock(block1) await node.wallet.scan() - const { block: blockA2, transaction } = await useBlockWithTx(node, accountA, accountB) - await expect(node.chain).toAddBlock(blockA2) - - const transactions = await node.chain.getBlockTransactions(blockA2.header) - await node.wallet.scanner.connectBlock( - blockA2.header, - transactions.map((t) => t.transaction), - ) - - const transactionValue = await accountA.getTransaction(transaction.hash()) + // create a second account and import it so that its head hash is null + const { node: nodeB } = await nodeTest.createSetup() + const toImport = await useAccountFixture(nodeB.wallet, 'accountB') - expect(transactionValue).toBeDefined() - expect(transactionValue?.blockHash).toEqualHash(blockA2.header.hash) - expect(transactionValue?.sequence).toEqual(blockA2.header.sequence) - }) + const accountB = await node.wallet.importAccount(toImport) - it('should update the account head hash', async () => { - const { node } = await nodeTest.createSetup() + // Confirm pre-rescan state + await expect(accountA.getHead()).resolves.toEqual({ + hash: block1.header.hash, + sequence: block1.header.sequence, + }) + await expect(accountB.getHead()).resolves.toEqual(null) - const accountA = await useAccountFixture(node.wallet, 'a') - const accountB = await useAccountFixture(node.wallet, 'b') + // Add second block + const block2 = await useMinerBlockFixture(node.chain, 3, accountA) + await expect(node.chain).toAddBlock(block2) - const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA1) await node.wallet.scan() - const { block: blockA2 } = await useBlockWithTx(node, accountA, accountB) - await expect(node.chain).toAddBlock(blockA2) + await expect(accountA.getHead()).resolves.toEqual({ + hash: block2.header.hash, + sequence: block2.header.sequence, + }) + await expect(accountB.getHead()).resolves.toEqual({ + hash: block2.header.hash, + sequence: block2.header.sequence, + }) + }) - const transactions = await node.chain.getBlockTransactions(blockA2.header) - await node.wallet.scanner.connectBlock( - blockA2.header, - transactions.map((t) => t.transaction), - ) + it('should not scan if wallet is disabled', async () => { + const { wallet, chain } = await nodeTest.createSetup({ config: { enableWallet: false } }) - const accountAHead = await accountA.getHead() + // Create a new account but don't give it an account birthday so the wallet head does not update + await useAccountFixture(wallet, 'test', { createdAt: null }) - expect(accountAHead?.hash).toEqualHash(blockA2.header.hash) + const block1 = await useMinerBlockFixture(chain) + await expect(chain).toAddBlock(block1) + + const scanSpy = jest.spyOn(wallet.scanner, 'scan') + await wallet.scan() + expect(scanSpy).not.toHaveBeenCalled() }) - it('should update the account unconfirmed balance', async () => { - const { node } = await nodeTest.createSetup() + it('should not scan if all accounts are up to date', async () => { + const { chain, wallet } = nodeTest - const accountA = await useAccountFixture(node.wallet, 'a') - const accountB = await useAccountFixture(node.wallet, 'b') + const accountA = await useAccountFixture(wallet, 'accountA') + const accountB = await useAccountFixture(wallet, 'accountB') - const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA1) - await node.wallet.scan() + const block1 = await useMinerBlockFixture(chain) + await expect(chain).toAddBlock(block1) - const balanceBefore = await accountA.getUnconfirmedBalance(Asset.nativeId()) - expect(balanceBefore.unconfirmed).toEqual(2000000000n) + await wallet.scan() - const { block: blockA2 } = await useBlockWithTx(node, accountA, accountB, false) - await expect(node.chain).toAddBlock(blockA2) + await expect(accountA.getHead()).resolves.toEqual({ + hash: block1.header.hash, + sequence: block1.header.sequence, + }) + await expect(accountB.getHead()).resolves.toEqual({ + hash: block1.header.hash, + sequence: block1.header.sequence, + }) - const transactions = await node.chain.getBlockTransactions(blockA2.header) - await node.wallet.scanner.connectBlock( - blockA2.header, - transactions.map((t) => t.transaction), - ) + const connectSpy = jest.spyOn(wallet, 'connectBlockForAccount') - const balanceAfter = await accountA.getUnconfirmedBalance(Asset.nativeId()) - expect(balanceAfter.unconfirmed).toEqual(1999999998n) + await wallet.scan() + + expect(connectSpy).not.toHaveBeenCalled() }) - it('should not connect blocks behind the account head', async () => { + it('should scan until the chain head', async () => { const { node } = await nodeTest.createSetup() - const accountA = await useAccountFixture(node.wallet, 'a') + // create an account so that the wallet will sync + await useAccountFixture(node.wallet, 'a') - const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA1) + // update wallet to genesis block await node.wallet.scan() - const blockA2 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA2) + const block2 = await useMinerBlockFixture(node.chain, undefined) + await expect(node.chain).toAddBlock(block2) + const block3 = await useMinerBlockFixture(node.chain, undefined) + await expect(node.chain).toAddBlock(block3) - const transactions = await node.chain.getBlockTransactions(blockA2.header) - await node.wallet.scanner.connectBlock( - blockA2.header, - transactions.map((t) => t.transaction), - ) - - let accountAHead = await accountA.getHead() - expect(accountAHead?.hash).toEqualHash(blockA2.header.hash) - - // Try to connect A2 again - await node.wallet.scanner.connectBlock( - blockA1.header, - transactions.map((t) => t.transaction), - ) - - // accountA head hash should be unchanged - accountAHead = await accountA.getHead() - expect(accountAHead?.hash).toEqualHash(blockA2.header.hash) - }) + expect(node.chain.head.hash).toEqualHash(block3.header.hash) - it('should not connect blocks equal to the account head', async () => { - const { node } = await nodeTest.createSetup() + let head = await node.wallet.getEarliestHead() + expect(head?.hash).toEqualHash(node.chain.genesis.hash) - const accountA = await useAccountFixture(node.wallet, 'a') + // set max syncing queue to 1 so that wallet only fetches one block at a time + node.wallet.scanner.config.set('walletSyncingMaxQueueSize', 1) - const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA1) await node.wallet.scan() - const blockA2 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA2) - - const transactionsA2 = await node.chain.getBlockTransactions(blockA2.header) - await node.wallet.scanner.connectBlock( - blockA2.header, - transactionsA2.map((t) => t.transaction), - ) - - let accountAHead = await accountA.getHead() - expect(accountAHead?.hash).toEqualHash(blockA2.header.hash) - - const updateHeadSpy = jest.spyOn(accountA, 'updateHead') - - // Try to connect A1 again - const transactionsA1 = await node.chain.getBlockTransactions(blockA1.header) - await node.wallet.scanner.connectBlock( - blockA1.header, - transactionsA1.map((t) => t.transaction), - ) - - expect(updateHeadSpy).not.toHaveBeenCalled() - - accountAHead = await accountA.getHead() - expect(accountAHead?.hash).toEqualHash(blockA2.header.hash) + head = await node.wallet.getEarliestHead() + expect(head?.hash).toEqualHash(node.chain.head.hash) }) - it('should not connect blocks more than one block ahead of the account head', async () => { - const { node } = await nodeTest.createSetup() + it('should start from null account head', async () => { + // G -> 1 -> 2 + const { node } = nodeTest - const accountA = await useAccountFixture(node.wallet, 'a') + const accountA = await useAccountFixture(node.wallet, 'accountA') - const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA1) + const block1 = await useMinerBlockFixture(node.chain, 2, accountA) + await expect(node.chain).toAddBlock(block1) await node.wallet.scan() - const blockA2 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA2) - const blockA3 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA3) + // create a second account and import it so that its head hash is null + const { node: nodeB } = await nodeTest.createSetup() + const toImport = await useAccountFixture(nodeB.wallet, 'accountB') - let accountAHead = await accountA.getHead() - expect(accountAHead?.hash).toEqualHash(blockA1.header.hash) + const accountB = await node.wallet.importAccount(toImport) - const updateHeadSpy = jest.spyOn(accountA, 'updateHead') + // Confirm pre-rescan state + await expect(accountA.getHead()).resolves.toEqual({ + hash: block1.header.hash, + sequence: block1.header.sequence, + }) + await expect(accountB.getHead()).resolves.toEqual(null) - // Try to connect A3 - const transactionsA3 = await node.chain.getBlockTransactions(blockA3.header) - await node.wallet.scanner.connectBlock( - blockA3.header, - transactionsA3.map((t) => t.transaction), - ) + // Add second block + const block2 = await useMinerBlockFixture(node.chain, 3, accountA) + await expect(node.chain).toAddBlock(block2) - expect(updateHeadSpy).not.toHaveBeenCalled() + await node.wallet.scan() - accountAHead = await accountA.getHead() - expect(accountAHead?.hash).toEqualHash(blockA1.header.hash) + await expect(accountA.getHead()).resolves.toEqual({ + hash: block2.header.hash, + sequence: block2.header.sequence, + }) + await expect(accountB.getHead()).resolves.toEqual({ + hash: block2.header.hash, + sequence: block2.header.sequence, + }) }) it('should update balance hash and sequence for each block', async () => { @@ -2046,51 +1868,6 @@ describe('Wallet', () => { expect(accountA.createdAt?.sequence).toEqual(block2.header.sequence) }) - it('should skip decryption for accounts with createdAt later than the block header', async () => { - const { node: nodeA } = await nodeTest.createSetup() - - let accountA: Account | null = await useAccountFixture(nodeA.wallet, 'a') - - const block2 = await useMinerBlockFixture(nodeA.chain, 2, undefined) - await nodeA.chain.addBlock(block2) - await nodeA.wallet.scan() - const block3 = await useMinerBlockFixture(nodeA.chain, 2, undefined) - await nodeA.chain.addBlock(block3) - await nodeA.wallet.scan() - - // create second account with createdAt at block 3 - const accountB = await useAccountFixture(nodeA.wallet, 'b') - - expect(accountB.createdAt).not.toBeNull() - expect(accountB.createdAt?.sequence).toEqual(block3.header.sequence) - - // reset wallet - await nodeA.wallet.reset() - - // account instances will have changed after reset, so re-load accountA - accountA = nodeA.wallet.getAccountByName('a') - Assert.isNotNull(accountA) - - const transactions = await nodeA.chain.getBlockTransactions(nodeA.chain.genesis) - await nodeA.wallet.scanner.connectBlock( - nodeA.chain.genesis, - transactions.map((t) => t.transaction), - ) - - const decryptSpy = jest.spyOn(nodeA.wallet, 'decryptNotes') - - // reconnect block2 - const transactions2 = await nodeA.chain.getBlockTransactions(block2.header) - await nodeA.wallet.scanner.connectBlock( - block2.header, - transactions2.map((t) => t.transaction), - ) - - // see that decryption was skipped for accountB - expect(decryptSpy).toHaveBeenCalledTimes(1) - expect(decryptSpy.mock.lastCall?.[3]).toEqual([accountA]) - }) - it('should skip updating accounts with scanningEnabled set to false', async () => { const { node } = await nodeTest.createSetup() const accountA: Account = await useAccountFixture(node.wallet, 'a') @@ -2112,51 +1889,7 @@ describe('Wallet', () => { expect(aHead.sequence).toBe(1) expect(bHead.sequence).toBe(3) }) - }) - - describe('getAssetStatus', () => { - it('should return the correct status for assets', async () => { - const { node } = await nodeTest.createSetup() - const account = await useAccountFixture(node.wallet) - - const mined = await useMinerBlockFixture(node.chain, 2, account) - await expect(node.chain).toAddBlock(mined) - await node.wallet.scan() - - const asset = new Asset(account.publicAddress, 'asset', 'metadata') - const value = BigInt(10) - const mintBlock = await useMintBlockFixture({ - node, - account, - asset, - value, - }) - - let assetValue = await node.wallet.walletDb.getAsset(account, asset.id()) - Assert.isNotUndefined(assetValue) - - // Check status before added to a block - expect(await node.wallet.getAssetStatus(account, assetValue)).toEqual(AssetStatus.PENDING) - - // Add to a block and check different confirmation ranges - await expect(node.chain).toAddBlock(mintBlock) - await node.wallet.scan() - assetValue = await node.wallet.walletDb.getAsset(account, asset.id()) - Assert.isNotUndefined(assetValue) - expect(await node.wallet.getAssetStatus(account, assetValue)).toEqual( - AssetStatus.CONFIRMED, - ) - expect( - await node.wallet.getAssetStatus(account, assetValue, { confirmations: 2 }), - ).toEqual(AssetStatus.UNCONFIRMED) - - // Remove the head and check status - jest.spyOn(account, 'getHead').mockResolvedValueOnce(Promise.resolve(null)) - expect(await node.wallet.getAssetStatus(account, assetValue)).toEqual(AssetStatus.UNKNOWN) - }) - }) - describe('disconnectBlock', () => { it('should update transactions in the walletDb with blockHash and sequence null', async () => { const { node } = await nodeTest.createSetup() @@ -2252,248 +1985,80 @@ describe('Wallet', () => { expect(balanceAfterDisconnect.unconfirmed).toEqual(2000000000n) }) - it('should not disconnect blocks before the account head', async () => { + it('should skip disconnecting for accounts with scanningEnabled set to false', async () => { const { node } = await nodeTest.createSetup() const accountA = await useAccountFixture(node.wallet, 'a') + const accountB = await useAccountFixture(node.wallet, 'b') const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) await expect(node.chain).toAddBlock(blockA1) - await node.wallet.scan() - const blockA2 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) await expect(node.chain).toAddBlock(blockA2) - await node.wallet.scan() - - let accountAHead = await accountA.getHead() - expect(accountAHead?.hash).toEqualHash(blockA2.header.hash) - // Try to disconnect blockA1 - const transactions = await node.chain.getBlockTransactions(blockA1.header) - await node.wallet.scanner.disconnectBlock( - blockA1.header, - transactions.map((t) => t.transaction), - ) - - // Verify accountA head hash unchanged - accountAHead = await accountA.getHead() - expect(accountAHead?.hash).toEqualHash(blockA2.header.hash) - }) - - it('should not disconnect blocks ahead of the account head', async () => { - const { node } = await nodeTest.createSetup() - - const accountA = await useAccountFixture(node.wallet, 'a') - - const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA1) await node.wallet.scan() - const blockA2 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await node.chain.addBlock(blockA2) - let accountAHead = await accountA.getHead() - expect(accountAHead?.hash).toEqualHash(blockA1.header.hash) - - const updateHeadSpy = jest.spyOn(accountA, 'updateHead') - - // Try to disconnect blockA2 - const transactions = await node.chain.getBlockTransactions(blockA2.header) - await node.wallet.scanner.disconnectBlock( - blockA2.header, - transactions.map((t) => t.transaction), - ) - - expect(updateHeadSpy).not.toHaveBeenCalled() - - accountAHead = await accountA.getHead() - expect(accountAHead?.hash).toEqualHash(blockA1.header.hash) - }) - - it('should remove minersFee transactions', async () => { - const { node } = await nodeTest.createSetup() - - const accountA = await useAccountFixture(node.wallet, 'a') - - const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - const transaction = blockA1.transactions[0] - - Assert.isTrue(transaction.isMinersFee()) - - await expect(node.chain).toAddBlock(blockA1) - await node.wallet.scan() - - const accountAHead = await accountA.getHead() - expect(accountAHead?.hash).toEqualHash(blockA1.header.hash) - await expect(accountA.hasTransaction(transaction.hash())).resolves.toEqual(true) - - // disconnect blockA1 - const transactions = await node.chain.getBlockTransactions(blockA1.header) - await node.wallet.scanner.disconnectBlock( - blockA1.header, - transactions.map((t) => t.transaction), - ) - - await expect(accountA.hasTransaction(transaction.hash())).resolves.toEqual(false) - }) - - it('should update balance hash and sequence for each block', async () => { - const { node } = await nodeTest.createSetup() + let accountBHead = await accountB.getHead() - const accountA = await useAccountFixture(node.wallet, 'a') - const accountB = await useAccountFixture(node.wallet, 'b') + expect(accountAHead?.hash).toEqualHash(blockA2.header.hash) + expect(accountBHead?.hash).toEqualHash(blockA2.header.hash) - const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA1) - await node.wallet.scan() + await accountA.updateScanningEnabled(false) - await expect(accountA.getUnconfirmedBalance(Asset.nativeId())).resolves.toMatchObject({ - blockHash: blockA1.header.hash, - sequence: blockA1.header.sequence, - unconfirmed: 2000000000n, - }) - await expect(accountB.getUnconfirmedBalance(Asset.nativeId())).resolves.toMatchObject({ - blockHash: blockA1.header.hash, - sequence: blockA1.header.sequence, - unconfirmed: 0n, + await node.chain.blockchainDb.db.transaction(async (tx) => { + await node.chain.disconnect(blockA2, tx) }) - const blockA2 = await useMinerBlockFixture(node.chain, undefined, accountB, node.wallet) - await expect(node.chain).toAddBlock(blockA2) await node.wallet.scan() - await expect(accountA.getUnconfirmedBalance(Asset.nativeId())).resolves.toMatchObject({ - blockHash: blockA2.header.hash, - sequence: blockA2.header.sequence, - unconfirmed: 2000000000n, - }) - await expect(accountB.getUnconfirmedBalance(Asset.nativeId())).resolves.toMatchObject({ - blockHash: blockA2.header.hash, - sequence: blockA2.header.sequence, - unconfirmed: 2000000000n, - }) - - const transactions = await node.chain.getBlockTransactions(blockA2.header) - await node.wallet.scanner.disconnectBlock( - blockA2.header, - transactions.map((t) => t.transaction), - ) + accountAHead = await accountA.getHead() + accountBHead = await accountB.getHead() - await expect(accountA.getUnconfirmedBalance(Asset.nativeId())).resolves.toMatchObject({ - blockHash: blockA1.header.hash, - sequence: blockA1.header.sequence, - unconfirmed: 2000000000n, - }) - await expect(accountB.getUnconfirmedBalance(Asset.nativeId())).resolves.toMatchObject({ - blockHash: blockA1.header.hash, - sequence: blockA1.header.sequence, - unconfirmed: 0n, - }) + expect(accountAHead?.hash).toEqualHash(blockA2.header.hash) + expect(accountBHead?.hash).toEqualHash(blockA2.header.previousBlockHash) }) + }) - it('should update balance hash and sequence for each asset in each block', async () => { + describe('getAssetStatus', () => { + it('should return the correct status for assets', async () => { const { node } = await nodeTest.createSetup() + const account = await useAccountFixture(node.wallet) - const accountA = await useAccountFixture(node.wallet, 'a') - - const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA1) + const mined = await useMinerBlockFixture(node.chain, 2, account) + await expect(node.chain).toAddBlock(mined) await node.wallet.scan() - await expect(accountA.getUnconfirmedBalance(Asset.nativeId())).resolves.toMatchObject({ - blockHash: blockA1.header.hash, - sequence: blockA1.header.sequence, - unconfirmed: 2000000000n, - }) - - const asset = new Asset(accountA.publicAddress, 'fakeasset', 'metadata') + const asset = new Asset(account.publicAddress, 'asset', 'metadata') const value = BigInt(10) const mintBlock = await useMintBlockFixture({ node, - account: accountA, + account, asset, value, - sequence: 3, }) - await expect(node.chain).toAddBlock(mintBlock) - await node.wallet.scan() - await expect(accountA.getUnconfirmedBalance(Asset.nativeId())).resolves.toMatchObject({ - blockHash: mintBlock.header.hash, - sequence: mintBlock.header.sequence, - unconfirmed: 2000000000n, - }) - await expect(accountA.getUnconfirmedBalance(asset.id())).resolves.toMatchObject({ - blockHash: mintBlock.header.hash, - sequence: mintBlock.header.sequence, - unconfirmed: value, - }) - - const blockA3 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA3) - await node.wallet.scan() - - await expect(accountA.getUnconfirmedBalance(Asset.nativeId())).resolves.toMatchObject({ - blockHash: blockA3.header.hash, - sequence: blockA3.header.sequence, - unconfirmed: 4000000000n, - }) - await expect(accountA.getUnconfirmedBalance(asset.id())).resolves.toMatchObject({ - blockHash: blockA3.header.hash, - sequence: blockA3.header.sequence, - unconfirmed: value, - }) - - const transactions = await node.chain.getBlockTransactions(blockA3.header) - await node.wallet.scanner.disconnectBlock( - blockA3.header, - transactions.map((t) => t.transaction), - ) - - await expect(accountA.getUnconfirmedBalance(Asset.nativeId())).resolves.toMatchObject({ - blockHash: mintBlock.header.hash, - sequence: mintBlock.header.sequence, - unconfirmed: 2000000000n, - }) - await expect(accountA.getUnconfirmedBalance(asset.id())).resolves.toMatchObject({ - blockHash: mintBlock.header.hash, - sequence: mintBlock.header.sequence, - unconfirmed: value, - }) - }) - - it('should skip disconnecting for accounts with scanningEnabled set to false', async () => { - const { node } = await nodeTest.createSetup() - - const accountA = await useAccountFixture(node.wallet, 'a') - const accountB = await useAccountFixture(node.wallet, 'b') - - const blockA1 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA1) - const blockA2 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) - await expect(node.chain).toAddBlock(blockA2) - - await node.wallet.scan() - - let accountAHead = await accountA.getHead() - let accountBHead = await accountB.getHead() - - expect(accountAHead?.hash).toEqualHash(blockA2.header.hash) - expect(accountBHead?.hash).toEqualHash(blockA2.header.hash) - - await accountA.updateScanningEnabled(false) + let assetValue = await node.wallet.walletDb.getAsset(account, asset.id()) + Assert.isNotUndefined(assetValue) - await node.chain.blockchainDb.db.transaction(async (tx) => { - await node.chain.disconnect(blockA2, tx) - }) + // Check status before added to a block + expect(await node.wallet.getAssetStatus(account, assetValue)).toEqual(AssetStatus.PENDING) + // Add to a block and check different confirmation ranges + await expect(node.chain).toAddBlock(mintBlock) await node.wallet.scan() + assetValue = await node.wallet.walletDb.getAsset(account, asset.id()) + Assert.isNotUndefined(assetValue) + expect(await node.wallet.getAssetStatus(account, assetValue)).toEqual( + AssetStatus.CONFIRMED, + ) + expect( + await node.wallet.getAssetStatus(account, assetValue, { confirmations: 2 }), + ).toEqual(AssetStatus.UNCONFIRMED) - accountAHead = await accountA.getHead() - accountBHead = await accountB.getHead() - - expect(accountAHead?.hash).toEqualHash(blockA2.header.hash) - expect(accountBHead?.hash).toEqualHash(blockA2.header.previousBlockHash) + // Remove the head and check status + jest.spyOn(account, 'getHead').mockResolvedValueOnce(Promise.resolve(null)) + expect(await node.wallet.getAssetStatus(account, assetValue)).toEqual(AssetStatus.UNKNOWN) }) }) diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index 8fce590faf..f366240ab2 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.ts @@ -146,6 +146,7 @@ export class Wallet { this.scanner = new WalletScanner({ wallet: this, + workerPool: this.workerPool, logger: this.logger, config: this.config, nodeClient: this.nodeClient, @@ -614,7 +615,6 @@ export class Wallet { for (const account of accounts) { const decryptedNotes = decryptedNotesByAccountId.get(account.id) ?? [] - await this.backfillAssets(account, decryptedNotes, transaction.mints) await account.addPendingTransaction(transaction, decryptedNotes, head.sequence) } diff --git a/ironfish/src/workerPool/pool.ts b/ironfish/src/workerPool/pool.ts index 57cabbc965..197f37194a 100644 --- a/ironfish/src/workerPool/pool.ts +++ b/ironfish/src/workerPool/pool.ts @@ -225,7 +225,7 @@ export class WorkerPool { await this.execute(request).result() } - private execute(request: Readonly): Job { + execute(request: Readonly): Job { // Ensure that workers are started before handling jobs this.start() From bf4472ffe67e7ab05f0e0a211984ef15eb18918e Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 20 Jun 2024 16:19:18 -0700 Subject: [PATCH 16/81] Fast construction of `NoteEncrypted` during wallet scans Wallet scans get the blocks/transactions/notes from the chain db. The chain db contains data that has already been validated in the past (or that otherwise is assumed to be valid). For this reason, it should be possible to speed up scans by skipping expensive validation performed when parsing notes. More specifically, `NoteEncrypted` parses some elliptic curve points in compressed form. The regular parsing done through `jubjub::SubgroupPoint::from_bytes()` involves an expensive check: it checks whether the uncompressed point belongs to the prime-order subgroup. This check is perfromed using elliptic curve point multiplication, which is the most predominant and slowest operation happening during wallet scans. Due to this check, for each note, the wallet currently performs two elliptic curve point multiplications: one to parse the note, another to attempt the actual decryption. By using `from_bytes_unchecked()` instead of `from_bytes()` we can skip the first multiplication, effectively improving note decryption performance by a factor of *almost* 2x. This change only affects the performance of wallet scans. Note parsing and decryption outside of a scan is unaffected at the moment. In the future, we can disable validation in other contexts as we see fit. --- ironfish-rust-nodejs/index.d.ts | 2 +- .../src/structs/note_encrypted.rs | 11 ++- ironfish-rust/src/merkle_note.rs | 71 ++++++++++++++++++- ironfish-rust/src/serializing/mod.rs | 10 +++ ironfish/src/primitives/noteEncrypted.ts | 17 ++++- ironfish/src/wallet/scanner/walletScanner.ts | 1 + .../src/workerPool/tasks/decryptNotes.test.ts | 1 + ironfish/src/workerPool/tasks/decryptNotes.ts | 19 +++-- 8 files changed, 119 insertions(+), 13 deletions(-) diff --git a/ironfish-rust-nodejs/index.d.ts b/ironfish-rust-nodejs/index.d.ts index 6d17ac5e50..c36c629d94 100644 --- a/ironfish-rust-nodejs/index.d.ts +++ b/ironfish-rust-nodejs/index.d.ts @@ -117,7 +117,7 @@ export class Asset { } export type NativeNoteEncrypted = NoteEncrypted export class NoteEncrypted { - constructor(jsBytes: Buffer) + constructor(jsBytes: Buffer, skipValidation?: boolean | undefined | null) serialize(): Buffer equals(other: NoteEncrypted): boolean /** diff --git a/ironfish-rust-nodejs/src/structs/note_encrypted.rs b/ironfish-rust-nodejs/src/structs/note_encrypted.rs index bace22c6df..190a8ab326 100644 --- a/ironfish-rust-nodejs/src/structs/note_encrypted.rs +++ b/ironfish-rust-nodejs/src/structs/note_encrypted.rs @@ -37,9 +37,16 @@ pub struct NativeNoteEncrypted { #[napi] impl NativeNoteEncrypted { #[napi(constructor)] - pub fn new(js_bytes: JsBuffer) -> Result { + pub fn new(js_bytes: JsBuffer, skip_validation: Option) -> Result { let bytes = js_bytes.into_value()?; - let note = MerkleNote::read(bytes.as_ref()).map_err(to_napi_err)?; + let skip_validation = skip_validation.unwrap_or(false); + + let note = if !skip_validation { + MerkleNote::read(bytes.as_ref()) + } else { + MerkleNote::read_unchecked(bytes.as_ref()) + } + .map_err(to_napi_err)?; Ok(NativeNoteEncrypted { note }) } diff --git a/ironfish-rust/src/merkle_note.rs b/ironfish-rust/src/merkle_note.rs index 8067e5d297..e88aaa434b 100644 --- a/ironfish-rust/src/merkle_note.rs +++ b/ironfish-rust/src/merkle_note.rs @@ -2,7 +2,11 @@ * 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/. */ -use crate::{errors::IronfishError, keys::EphemeralKeyPair, serializing::read_point}; +use crate::{ + errors::IronfishError, + keys::EphemeralKeyPair, + serializing::{read_point, read_point_unchecked}, +}; /// Implement a merkle note to store all the values that need to go into a merkle tree. /// A tree containing these values can serve as a snapshot of the entire chain. @@ -39,7 +43,7 @@ pub const NOTE_ENCRYPTION_MINER_KEYS: &[u8; NOTE_ENCRYPTION_KEY_SIZE] = b"Iron Fish note encryption miner key000000000000000000000000000000000000000000000"; const SHARED_KEY_PERSONALIZATION: &[u8; 16] = b"Iron Fish Keyenc"; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct MerkleNote { /// Randomized value commitment. Sometimes referred to as /// `cv` in the literature. It's calculated by multiplying a value by a @@ -145,7 +149,7 @@ impl MerkleNote { } } - /// Load a MerkleNote from the given stream + /// Load a MerkleNote from the given reader. pub fn read(mut reader: R) -> Result { let value_commitment = read_point(&mut reader)?; let note_commitment = read_scalar(&mut reader)?; @@ -165,6 +169,30 @@ impl MerkleNote { }) } + /// Load a MerkleNote from the given reader, skipping some expensive validity checks on the + /// input. + /// + /// This method is faster than [`read()`], but it requires trusted or pre-validated input. + /// Passing invalid input may result in erroneous calculations or security risks. + pub fn read_unchecked(mut reader: R) -> Result { + let value_commitment = read_point_unchecked(&mut reader)?; + let note_commitment = read_scalar(&mut reader)?; + let ephemeral_public_key = read_point_unchecked(&mut reader)?; + + let mut encrypted_note = [0; ENCRYPTED_NOTE_SIZE + aead::MAC_SIZE]; + reader.read_exact(&mut encrypted_note[..])?; + let mut note_encryption_keys = [0; NOTE_ENCRYPTION_KEY_SIZE]; + reader.read_exact(&mut note_encryption_keys[..])?; + + Ok(MerkleNote { + value_commitment, + note_commitment, + ephemeral_public_key, + encrypted_note, + note_encryption_keys, + }) + } + pub fn write(&self, writer: &mut W) -> Result<(), IronfishError> { writer.write_all(&self.value_commitment.to_bytes())?; writer.write_all(&self.note_commitment.to_bytes_le())?; @@ -532,4 +560,41 @@ mod test { .decrypt_note_for_spender(spender_key.outgoing_view_key()) .is_err()); } + + #[test] + fn test_serialization_roundtrip() { + let spender_key = SaplingKey::generate_key(); + let note = Note::new( + spender_key.public_address(), + 42, + "", + NATIVE_ASSET, + spender_key.public_address(), + ); + let diffie_hellman_keys = EphemeralKeyPair::new(); + + let value_commitment = ValueCommitment::new(note.value, note.asset_generator()); + + let merkle_note = MerkleNote::new( + spender_key.outgoing_view_key(), + ¬e, + &value_commitment, + &diffie_hellman_keys, + ); + + let mut serialization = Vec::new(); + + merkle_note + .write(&mut serialization) + .expect("serialization failed"); + + assert_eq!( + MerkleNote::read(&serialization[..]).expect("deserialization failed"), + merkle_note + ); + assert_eq!( + MerkleNote::read_unchecked(&serialization[..]).expect("deserialization failed"), + merkle_note + ); + } } diff --git a/ironfish-rust/src/serializing/mod.rs b/ironfish-rust/src/serializing/mod.rs index 1555eaf690..17c671c1e7 100644 --- a/ironfish-rust/src/serializing/mod.rs +++ b/ironfish-rust/src/serializing/mod.rs @@ -34,6 +34,16 @@ pub(crate) fn read_point(mut reader: R) -> Result .ok_or_else(|| IronfishError::new(IronfishErrorKind::InvalidData)) } +pub(crate) fn read_point_unchecked( + mut reader: R, +) -> Result { + let mut point_repr = G::Repr::default(); + reader.read_exact(point_repr.as_mut())?; + + Option::from(G::from_bytes_unchecked(&point_repr)) + .ok_or_else(|| IronfishError::new(IronfishErrorKind::InvalidData)) +} + /// Output the bytes as a hexadecimal String pub fn bytes_to_hex(bytes: &[u8]) -> String { let mut hex: Vec = vec![0; bytes.len() * 2]; diff --git a/ironfish/src/primitives/noteEncrypted.ts b/ironfish/src/primitives/noteEncrypted.ts index 1ff5fd4492..8a58c30395 100644 --- a/ironfish/src/primitives/noteEncrypted.ts +++ b/ironfish/src/primitives/noteEncrypted.ts @@ -22,9 +22,16 @@ export class NoteEncrypted { private noteEncrypted: NativeNoteEncrypted | null = null private referenceCount = 0 - - constructor(noteEncryptedSerialized: Buffer) { + /** + * Used to record whether the note has already been previously validated, and + * thus does not need to be checked anymore after parsing. Used to speed up + * construction of `NativeNoteEncrypted` in `takeReference`. + */ + private skipValidation: boolean + + constructor(noteEncryptedSerialized: Buffer, options?: { skipValidation?: boolean }) { this.noteEncryptedSerialized = noteEncryptedSerialized + this.skipValidation = options?.skipValidation ?? false const reader = bufio.read(noteEncryptedSerialized, true) @@ -57,7 +64,11 @@ export class NoteEncrypted { takeReference(): NativeNoteEncrypted { this.referenceCount++ if (this.noteEncrypted === null) { - this.noteEncrypted = new NativeNoteEncrypted(this.noteEncryptedSerialized) + this.noteEncrypted = new NativeNoteEncrypted( + this.noteEncryptedSerialized, + this.skipValidation, + ) + this.skipValidation = true } return this.noteEncrypted } diff --git a/ironfish/src/wallet/scanner/walletScanner.ts b/ironfish/src/wallet/scanner/walletScanner.ts index 5fcd894d59..b0a3b67472 100644 --- a/ironfish/src/wallet/scanner/walletScanner.ts +++ b/ironfish/src/wallet/scanner/walletScanner.ts @@ -82,6 +82,7 @@ export class WalletScanner { const decryptor = new BackgroundNoteDecryptor(this.workerPool, this.config, { decryptForSpender: false, + skipNoteValidation: true, }) const chainProcessor = this.getChainProcessor(start) diff --git a/ironfish/src/workerPool/tasks/decryptNotes.test.ts b/ironfish/src/workerPool/tasks/decryptNotes.test.ts index f3063c9e0f..fd8d9ee536 100644 --- a/ironfish/src/workerPool/tasks/decryptNotes.test.ts +++ b/ironfish/src/workerPool/tasks/decryptNotes.test.ts @@ -32,6 +32,7 @@ describe('DecryptNotesRequest', () => { ], { decryptForSpender: true, + skipNoteValidation: false, }, 0, ) diff --git a/ironfish/src/workerPool/tasks/decryptNotes.ts b/ironfish/src/workerPool/tasks/decryptNotes.ts index 70c170d6f4..033e5c92e8 100644 --- a/ironfish/src/workerPool/tasks/decryptNotes.ts +++ b/ironfish/src/workerPool/tasks/decryptNotes.ts @@ -12,6 +12,7 @@ import { WorkerTask } from './workerTask' export interface DecryptNotesOptions { decryptForSpender: boolean + skipNoteValidation?: boolean } export interface DecryptNotesAccountKey { @@ -53,9 +54,12 @@ export class DecryptNotesRequest extends WorkerMessage { } serializePayload(bw: bufio.StaticWriter | bufio.BufferWriter): void { - bw.writeU8(this.options.decryptForSpender ? 1 : 0) - bw.writeU32(this.accountKeys.length) + const flags = + (Number(this.options.decryptForSpender) << 0) | + (Number(this.options.skipNoteValidation ?? false) << 1) + bw.writeU8(flags) + bw.writeU32(this.accountKeys.length) for (const key of this.accountKeys) { bw.writeBytes(Buffer.from(key.incomingViewKey, 'hex')) bw.writeBytes(Buffer.from(key.outgoingViewKey, 'hex')) @@ -71,9 +75,14 @@ export class DecryptNotesRequest extends WorkerMessage { static deserializePayload(jobId: number, buffer: Buffer): DecryptNotesRequest { const reader = bufio.read(buffer, true) + const flags = reader.readU8() + const options = { + decryptForSpender: !!(flags & (1 << 0)), + skipNoteValidation: !!(flags & (1 << 1)), + } + const accountKeys = [] const encryptedNotes = [] - const options = { decryptForSpender: reader.readU8() !== 0 } const keysLength = reader.readU32() for (let i = 0; i < keysLength; i++) { @@ -272,7 +281,9 @@ export class DecryptNotesTask extends WorkerTask { const decryptedNotes = [] for (const { serializedNote, currentNoteIndex } of encryptedNotes) { - const note = new NoteEncrypted(serializedNote) + const note = new NoteEncrypted(serializedNote, { + skipValidation: options.skipNoteValidation, + }) for (const { incomingViewKey, outgoingViewKey, viewKey } of accountKeys) { // Try decrypting the note as the owner From e62e36c0495000369c1c5e86cf5ce0624884dc74 Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Mon, 1 Jul 2024 12:45:56 -0700 Subject: [PATCH 17/81] add_signature to unsigned transaction (#5096) This feature will be used to attach signatures to unsigned transactions. The main usecase is to allow the user to sign the transaction offline and then submit it to the network. The offline signing will happen on ledger devices. --- ironfish-rust-nodejs/index.d.ts | 1 + .../src/structs/transaction.rs | 19 +++++ ironfish-rust/src/transaction/tests.rs | 82 ++++++++++++++++++- ironfish-rust/src/transaction/unsigned.rs | 12 ++- 4 files changed, 109 insertions(+), 5 deletions(-) diff --git a/ironfish-rust-nodejs/index.d.ts b/ironfish-rust-nodejs/index.d.ts index c36c629d94..886bf0e06d 100644 --- a/ironfish-rust-nodejs/index.d.ts +++ b/ironfish-rust-nodejs/index.d.ts @@ -230,6 +230,7 @@ export class UnsignedTransaction { hash(): Buffer signingPackage(nativeIdentiferCommitments: Array): string sign(spenderHexKey: string): Buffer + addSignature(signature: Buffer): Buffer } export class FoundBlockResult { randomness: string diff --git a/ironfish-rust-nodejs/src/structs/transaction.rs b/ironfish-rust-nodejs/src/structs/transaction.rs index 0f381aad1a..35806ce2c5 100644 --- a/ironfish-rust-nodejs/src/structs/transaction.rs +++ b/ironfish-rust-nodejs/src/structs/transaction.rs @@ -460,6 +460,25 @@ impl NativeUnsignedTransaction { Ok(Buffer::from(vec)) } + + #[napi] + pub fn add_signature(&mut self, signature: JsBuffer) -> Result { + let bytes = signature.into_value()?; + + let mut signature_bytes = [0u8; 64]; + + signature_bytes.copy_from_slice(bytes.as_ref()); + + let signed_transaction = self + .transaction + .add_signature(signature_bytes) + .map_err(to_napi_err)?; + + let mut vec: Vec = vec![]; + signed_transaction.write(&mut vec).map_err(to_napi_err)?; + + Ok(Buffer::from(vec)) + } } #[napi(namespace = "multisig")] diff --git a/ironfish-rust/src/transaction/tests.rs b/ironfish-rust/src/transaction/tests.rs index 5e24aef5a1..fa3e05ae9a 100644 --- a/ironfish-rust/src/transaction/tests.rs +++ b/ironfish-rust/src/transaction/tests.rs @@ -6,7 +6,9 @@ use std::collections::{BTreeMap, HashMap}; #[cfg(test)] use super::internal_batch_verify_transactions; -use super::{ProposedTransaction, Transaction}; + +use super::{ProposedTransaction, Transaction, TRANSACTION_PUBLIC_KEY_SIZE}; + use crate::test_util::create_multisig_identities; use crate::transaction::tests::split_spender_key::split_spender_key; use crate::{ @@ -23,8 +25,8 @@ use crate::{ TRANSACTION_EXPIRATION_SIZE, TRANSACTION_FEE_SIZE, TRANSACTION_SIGNATURE_SIZE, }, }; - use ff::Field; +use group::GroupEncoding; use ironfish_frost::{ frost::{round2, round2::SignatureShare, Identifier, Randomizer}, nonces::deterministic_signing_nonces, @@ -837,3 +839,79 @@ fn test_aggregate_signature_shares() { // verify transaction verify_transaction(&signed_transaction).expect("should be able to verify transaction"); } + +#[test] +fn test_add_signature_by_building_transaction() { + let spender_key = SaplingKey::generate_key(); + + // create notes + + let in_note = Note::new( + spender_key.public_address(), + 42, + "", + NATIVE_ASSET, + spender_key.public_address(), + ); + + let out_note = Note::new( + spender_key.public_address(), + 40, + "", + NATIVE_ASSET, + spender_key.public_address(), + ); + + let mut transaction = ProposedTransaction::new(TransactionVersion::latest()); + + transaction + .add_spend(in_note.clone(), &make_fake_witness(&in_note.clone())) + .unwrap(); + + transaction.add_output(out_note).unwrap(); + + let public_address: crate::PublicAddress = spender_key.public_address(); + + let intended_fee = 1; + + let mut unsigned_transaction = transaction + .build( + spender_key.proof_authorizing_key, + spender_key.view_key().clone(), + spender_key.outgoing_view_key().clone(), + intended_fee, + Some(public_address), + ) + .expect("should be able to build unsigned transaction"); + + let private_key = redjubjub::PrivateKey(spender_key.spend_authorizing_key); + let randomized_private_key = private_key.randomize(unsigned_transaction.public_key_randomness); + let transaction_hash_bytes = unsigned_transaction.transaction_signature_hash().unwrap(); + + let transaction_randomized_public_key = + redjubjub::PublicKey(spender_key.view_key.authorizing_key.into()).randomize( + unsigned_transaction.public_key_randomness, + *SPENDING_KEY_GENERATOR, + ); + + let mut data_to_be_signed = [0; 64]; + data_to_be_signed[..TRANSACTION_PUBLIC_KEY_SIZE] + .copy_from_slice(&transaction_randomized_public_key.0.to_bytes()); + data_to_be_signed[32..].copy_from_slice(&transaction_hash_bytes[..]); + + let signature = randomized_private_key.sign( + &data_to_be_signed, + &mut thread_rng(), + *SPENDING_KEY_GENERATOR, + ); + + let mut signature_bytes: [u8; 64] = [0; 64]; + + signature.write(signature_bytes.as_mut()).unwrap(); + + let signed = unsigned_transaction + .add_signature(signature_bytes) + .expect("should be able to sign transaction"); + + verify_transaction(&signed).expect("should be able to verify transaction"); +} diff --git a/ironfish-rust/src/transaction/unsigned.rs b/ironfish-rust/src/transaction/unsigned.rs index e89d45f2b4..ac2548d14b 100644 --- a/ironfish-rust/src/transaction/unsigned.rs +++ b/ironfish-rust/src/transaction/unsigned.rs @@ -227,15 +227,21 @@ impl UnsignedTransaction { IronfishError::new_with_source(IronfishErrorKind::FailedSignatureVerification, e) })?; - let signature = { Signature::read(&mut authorizing_group_signature.serialize().as_ref())? }; + let serialized_signature = authorizing_group_signature.serialize(); + + let transaction = self.add_signature(serialized_signature)?; + + Ok(transaction) + } + + pub fn add_signature(&mut self, signature: [u8; 64]) -> Result { + let signature = Signature::read(&signature[..])?; - // Sign spends now that we have the data needed to be signed let mut spend_descriptions = Vec::with_capacity(self.spends.len()); for spend in self.spends.drain(0..) { spend_descriptions.push(spend.add_signature(signature)); } - // Sign mints now that we have the data needed to be signed let mut mint_descriptions = Vec::with_capacity(self.mints.len()); for mint in self.mints.drain(0..) { mint_descriptions.push(mint.add_signature(signature)); From 8e6dfdadd90d3a91c91783bd44ba07acdc120b4c Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Mon, 1 Jul 2024 15:42:26 -0700 Subject: [PATCH 18/81] wallet/adSignature: RPC to add signature to a transaction. (#5097) This RPC is used to add signature to a transaction. It takes a transaction and a signature as input and returns a signed transaction. --- ironfish/src/rpc/clients/client.ts | 10 ++++ .../__fixtures__/addSignature.test.ts.fixture | 32 +++++++++++ .../rpc/routes/wallet/addSignature.test.ts | 36 +++++++++++++ .../src/rpc/routes/wallet/addSignature.ts | 53 +++++++++++++++++++ ironfish/src/rpc/routes/wallet/index.ts | 5 +- 5 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 ironfish/src/rpc/routes/wallet/__fixtures__/addSignature.test.ts.fixture create mode 100644 ironfish/src/rpc/routes/wallet/addSignature.test.ts create mode 100644 ironfish/src/rpc/routes/wallet/addSignature.ts diff --git a/ironfish/src/rpc/clients/client.ts b/ironfish/src/rpc/clients/client.ts index 84bb664d48..4dcce51d15 100644 --- a/ironfish/src/rpc/clients/client.ts +++ b/ironfish/src/rpc/clients/client.ts @@ -172,6 +172,7 @@ import type { UseAccountResponse, } from '../routes' import { ApiNamespace } from '../routes/namespaces' +import { AddSignatureRequest, AddSignatureResponse } from '../routes/wallet/addSignature' export abstract class RpcClient { abstract request( @@ -555,6 +556,15 @@ export abstract class RpcClient { ).waitForEnd() }, + addSignature: ( + params: AddSignatureRequest, + ): Promise> => { + return this.request( + `${ApiNamespace.wallet}/addSignature`, + params, + ).waitForEnd() + }, + createTransaction: ( params: CreateTransactionRequest, ): Promise> => { diff --git a/ironfish/src/rpc/routes/wallet/__fixtures__/addSignature.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/addSignature.test.ts.fixture new file mode 100644 index 0000000000..47c40f17fa --- /dev/null +++ b/ironfish/src/rpc/routes/wallet/__fixtures__/addSignature.test.ts.fixture @@ -0,0 +1,32 @@ +{ + "Route wallet/addSignature should return error if signature is not a valid hex": [ + { + "value": { + "version": 4, + "id": "0e9f1f07-ee9d-4746-a4e8-131d89d71345", + "name": "addSignatureAccount2", + "spendingKey": "21557df84c37fa55363c77ffe99a6d01a1c1938f06b805b34a2d2f3442016d3c", + "viewKey": "4d8f669c3667195906dcc4e636983d653e6042267fffb3b7ed1a00e8aeacf7011382ac8e43e6f446f3751dca9640eb7d684b88c181feec8f1399863b0e389c95", + "incomingViewKey": "9f13a57239aba9daade572166257ae494807e6436a9eaaf5bf312855e225d906", + "outgoingViewKey": "5c156963119e3fbdccd84d08622eae4dbbc36ada56e30682188a338fe9be0ed6", + "publicAddress": "608569ba934ef7bb6c6bc99e653b7ab18eb17bf02602c8fc489c3286d1065f2c", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "0b3bcdc835ee3709c9a30cad80c36d0d4f5d5712349d6bbfd34da9ed405bbc02" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + } + ] +} \ No newline at end of file diff --git a/ironfish/src/rpc/routes/wallet/addSignature.test.ts b/ironfish/src/rpc/routes/wallet/addSignature.test.ts new file mode 100644 index 0000000000..9d57c1ca49 --- /dev/null +++ b/ironfish/src/rpc/routes/wallet/addSignature.test.ts @@ -0,0 +1,36 @@ +/* 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 { RawTransactionSerde } from '../../../primitives' +import { useAccountFixture } from '../../../testUtilities' +import { createRawTransaction } from '../../../testUtilities/helpers/transaction' +import { createRouteTest } from '../../../testUtilities/routeTest' + +describe('Route wallet/addSignature', () => { + const routeTest = createRouteTest(true) + + it('should return error if signature is not a valid hex', async () => { + const account = await useAccountFixture(routeTest.node.wallet, 'addSignatureAccount') + const rawTransaction = await createRawTransaction({ + wallet: routeTest.node.wallet, + from: account, + }) + + const response = await routeTest.client.wallet.buildTransaction({ + rawTransaction: RawTransactionSerde.serialize(rawTransaction).toString('hex'), + account: account.name, + }) + + expect(response.status).toBe(200) + expect(response.content.unsignedTransaction).toBeDefined() + + const invalidSignature = 'invalid' + + await expect( + routeTest.client.wallet.addSignature({ + unsignedTransaction: response.content.unsignedTransaction, + signature: invalidSignature, + }), + ).rejects.toThrow('Invalid signature length') + }) +}) diff --git a/ironfish/src/rpc/routes/wallet/addSignature.ts b/ironfish/src/rpc/routes/wallet/addSignature.ts new file mode 100644 index 0000000000..122712f97a --- /dev/null +++ b/ironfish/src/rpc/routes/wallet/addSignature.ts @@ -0,0 +1,53 @@ +/* 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 { UnsignedTransaction } from '@ironfish/rust-nodejs' +import * as yup from 'yup' +import { ApiNamespace } from '../namespaces' +import { routes } from '../router' +import { AssertHasRpcContext } from '../rpcContext' + +export type AddSignatureRequest = { + unsignedTransaction: string + signature: string +} + +export type AddSignatureResponse = { + transaction: string +} + +export const AddSignatureRequestSchema: yup.ObjectSchema = yup + .object({ + unsignedTransaction: yup.string().defined(), + signature: yup.string().defined(), + }) + .defined() + +export const AddSignatureResponseSchema: yup.ObjectSchema = yup + .object({ + transaction: yup.string().defined(), + }) + .defined() + +routes.register( + `${ApiNamespace.wallet}/addSignature`, + AddSignatureRequestSchema, + (request, node): void => { + AssertHasRpcContext(request, node, 'wallet') + const unsignedTransaction = new UnsignedTransaction( + Buffer.from(request.data.unsignedTransaction, 'hex'), + ) + + const buffer = Buffer.from(request.data.signature, 'hex') + + if (buffer.length !== 64) { + throw new Error('Invalid signature length') + } + + const serialized = unsignedTransaction.addSignature(buffer) + + request.end({ + transaction: serialized.toString('hex'), + }) + }, +) diff --git a/ironfish/src/rpc/routes/wallet/index.ts b/ironfish/src/rpc/routes/wallet/index.ts index 446008ccb1..742c56b7a2 100644 --- a/ironfish/src/rpc/routes/wallet/index.ts +++ b/ironfish/src/rpc/routes/wallet/index.ts @@ -2,7 +2,7 @@ * 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/. */ -export * from './setAccountHead' +export * from './addSignature' export * from './addTransaction' export * from './buildTransaction' export * from './burnAsset' @@ -12,10 +12,10 @@ export * from './createTransaction' export * from './estimateFeeRates' export * from './exportAccount' export * from './getAccountNotesStream' -export * from './getAccounts' export * from './getAccountStatus' export * from './getAccountTransaction' export * from './getAccountTransactions' +export * from './getAccounts' export * from './getAccountsStatus' export * from './getAsset' export * from './getAssets' @@ -38,6 +38,7 @@ export * from './renameAccount' export * from './rescan' export * from './resetAccount' export * from './sendTransaction' +export * from './setAccountHead' export * from './setScanning' export * from './types' export * from './use' From e22b3b819e5d1db23b71abf4d23b18ed2f8c058f Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Mon, 1 Jul 2024 22:57:56 -0400 Subject: [PATCH 19/81] Wallet cleanup should use an iter to load records (#5098) In the case that the records to cleanup is very high, we shouldn't load all of them in memory before cleaning up. We should only load 1 leveldb memory block at most. This will ensure if there is a bad feedback loop this list won't permanently grow and continue to use more memory as it tries to clean up accounts. This also ensures that the account record deletion is considered a record so deleting thousands of empty account cleanup records doesn't run forever. --- ironfish/src/wallet/wallet.ts | 1 + ironfish/src/wallet/walletdb/walletdb.ts | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index f366240ab2..4184076fcb 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.ts @@ -1478,6 +1478,7 @@ export class Wallet { async removeAccount(account: Account, tx?: IDatabaseTransaction): Promise { this.accounts.delete(account.id) + await this.walletDb.db.withTransaction(tx, async (tx) => { if (account.id === this.defaultAccount) { await this.walletDb.setDefaultAccount(null, tx) diff --git a/ironfish/src/wallet/walletdb/walletdb.ts b/ironfish/src/wallet/walletdb/walletdb.ts index 3020e64384..9853f521af 100644 --- a/ironfish/src/wallet/walletdb/walletdb.ts +++ b/ironfish/src/wallet/walletdb/walletdb.ts @@ -1129,13 +1129,13 @@ export class WalletDB { } async cleanupDeletedAccounts(recordsToCleanup: number, signal?: AbortSignal): Promise { - for (const [accountId] of await this.accountIdsToCleanup.getAll()) { + for await (const [accountId] of this.accountIdsToCleanup.getAllIter()) { const prefix = calculateAccountPrefix(accountId) const range = StorageUtils.getPrefixKeyRange(prefix) for (const store of this.cacheStores) { for await (const key of store.getAllKeysIter(undefined, range)) { - if (signal?.aborted === true || recordsToCleanup === 0) { + if (signal?.aborted === true || recordsToCleanup <= 0) { return } @@ -1145,6 +1145,7 @@ export class WalletDB { } await this.accountIdsToCleanup.del(accountId) + recordsToCleanup-- } } From 82406d98531c8520c49edf1456c596bf0a8bc52d Mon Sep 17 00:00:00 2001 From: Derek Guenther Date: Tue, 2 Jul 2024 13:15:18 -0400 Subject: [PATCH 20/81] Add lint rule to not use Promise.race (#5103) --- config/eslint-config-ironfish/index.js | 1 + config/eslint-plugin-ironfish/index.js | 15 ++++++++ ironfish/src/utils/asyncQueue.test.ts | 13 +++++-- ironfish/src/wallet/scanner/noteDecryptor.ts | 36 +++++++++++++++----- 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/config/eslint-config-ironfish/index.js b/config/eslint-config-ironfish/index.js index 978bcb75d7..d0747feae8 100644 --- a/config/eslint-config-ironfish/index.js +++ b/config/eslint-config-ironfish/index.js @@ -68,6 +68,7 @@ module.exports = { rules: { 'ironfish/no-vague-imports': 'error', 'ironfish/no-buffer-cmp': 'error', + 'ironfish/no-promise-race': 'error', // Catches expressions that aren't assigned '@typescript-eslint/no-unused-expressions': [ diff --git a/config/eslint-plugin-ironfish/index.js b/config/eslint-plugin-ironfish/index.js index 0ff7d24290..8b189ee5b5 100644 --- a/config/eslint-plugin-ironfish/index.js +++ b/config/eslint-plugin-ironfish/index.js @@ -42,4 +42,19 @@ module.exports.rules = { }; }, }, + "no-promise-race": { + create(context) { + return { + MemberExpression: function (node) { + if (node.object.name === 'Promise' && node.property.name === 'race') { + context.report({ + node, + message: + "Promise.race leaks memory. You can work around it by using PromiseUtils.split to pass resolve/reject to other Promises. See https://github.com/nodejs/node/issues/17469#issuecomment-685216777 for more details.", + }); + } + }, + }; + }, + }, }; diff --git a/ironfish/src/utils/asyncQueue.test.ts b/ironfish/src/utils/asyncQueue.test.ts index 52e9239597..582a7a03d1 100644 --- a/ironfish/src/utils/asyncQueue.test.ts +++ b/ironfish/src/utils/asyncQueue.test.ts @@ -2,6 +2,7 @@ * 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 { AsyncQueue } from './asyncQueue' +import { PromiseUtils } from './promise' describe('AsyncQueue', () => { it('yields items in the same order as they were added', async () => { @@ -81,7 +82,11 @@ describe('AsyncQueue', () => { () => 'otherPromise', ) - const resolved = await Promise.race([pushPromise, otherPromise]) + const [promise, res, rej] = PromiseUtils.split() + pushPromise.then(res, rej) + otherPromise.then(res, rej) + + const resolved = await promise expect(resolved).toBe('otherPromise') // After popping an element, the promise returned by push should resolve @@ -101,7 +106,11 @@ describe('AsyncQueue', () => { () => 'otherPromise', ) - const resolved = await Promise.race([popPromise, otherPromise]) + const [promise, res, rej] = PromiseUtils.split() + popPromise.then(res, rej) + otherPromise.then(res, rej) + + const resolved = await promise expect(resolved).toBe('otherPromise') // After pushing a new element, the promise returned by pop should diff --git a/ironfish/src/wallet/scanner/noteDecryptor.ts b/ironfish/src/wallet/scanner/noteDecryptor.ts index 3c80aed5dc..d74afc3de8 100644 --- a/ironfish/src/wallet/scanner/noteDecryptor.ts +++ b/ironfish/src/wallet/scanner/noteDecryptor.ts @@ -6,6 +6,7 @@ import { Config } from '../../fileStores' import { BlockHeader } from '../../primitives' import { Transaction } from '../../primitives/transaction' import { AsyncQueue } from '../../utils/asyncQueue' +import { PromiseUtils } from '../../utils/promise' import { WorkerPool } from '../../workerPool' import { Job } from '../../workerPool/job' import { @@ -23,6 +24,14 @@ export type DecryptNotesFromTransactionsCallback = ( transactions: Array<{ transaction: Transaction; decryptedNotes: Array }>, ) => Promise +type DecryptQueueValue = { + job: Job + accounts: ReadonlyArray + blockHeader: BlockHeader + transactions: ReadonlyArray + callback: DecryptNotesFromTransactionsCallback +} + export class BackgroundNoteDecryptor { private isStarted = false @@ -34,13 +43,7 @@ export class BackgroundNoteDecryptor { private readonly workerPool: WorkerPool private readonly options: DecryptNotesOptions - private readonly decryptQueue: AsyncQueue<{ - job: Job - accounts: ReadonlyArray - blockHeader: BlockHeader - transactions: ReadonlyArray - callback: DecryptNotesFromTransactionsCallback - }> + private readonly decryptQueue: AsyncQueue constructor(workerPool: WorkerPool, config: Config, options: DecryptNotesOptions) { this.workerPool = workerPool @@ -81,13 +84,30 @@ export class BackgroundNoteDecryptor { } private async decryptLoop(): Promise { + let resolve: (value: DecryptQueueValue | void) => unknown + let reject: (reason?: unknown) => void + + this.onStopped.then( + (value) => resolve?.(value), + (err) => reject?.(err), + ) + while (this.isStarted) { if (this.decryptQueue.isEmpty() && this.triggerFlushed) { this.triggerFlushed() this.triggerFlushed = null } - const item = await Promise.race([this.decryptQueue.pop(), this.onStopped]) + const [promise, resolveNew, rejectNew] = PromiseUtils.split() + resolve = resolveNew + reject = rejectNew + + this.decryptQueue.pop().then( + (value) => resolve(value), + (err) => reject(err), + ) + + const item = await promise if (!item) { break } From 37c42fc1ddb98a31247fc575f5a42ac4215f2649 Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Tue, 2 Jul 2024 12:14:34 -0700 Subject: [PATCH 21/81] adds ledger utility class (#5099) * adds ledger utility class * sorting package.json and using lastest library: * updating lock --- ironfish-cli/package.json | 2 + ironfish-cli/src/utils/ledger.ts | 150 ++++++++++++++++++++++++++++++ yarn.lock | 151 ++++++++++++++++++++++++++++++- 3 files changed, 300 insertions(+), 3 deletions(-) create mode 100644 ironfish-cli/src/utils/ledger.ts diff --git a/ironfish-cli/package.json b/ironfish-cli/package.json index 0dd32d2ebb..b15b7a6319 100644 --- a/ironfish-cli/package.json +++ b/ironfish-cli/package.json @@ -60,6 +60,7 @@ "dependencies": { "@ironfish/rust-nodejs": "2.4.0", "@ironfish/sdk": "2.4.0", + "@ledgerhq/hw-transport-node-hid": "6.28.6", "@oclif/core": "3.27.0", "@oclif/plugin-autocomplete": "1.3.10", "@oclif/plugin-help": "5.1.12", @@ -67,6 +68,7 @@ "@oclif/plugin-warn-if-update-available": "2.0.40", "@types/keccak": "3.0.4", "@types/tar": "6.1.1", + "@zondax/ledger-ironfish": "0.1.2", "axios": "1.7.2", "bech32": "2.0.0", "blessed": "0.1.81", diff --git a/ironfish-cli/src/utils/ledger.ts b/ironfish-cli/src/utils/ledger.ts new file mode 100644 index 0000000000..0ebfef5e07 --- /dev/null +++ b/ironfish-cli/src/utils/ledger.ts @@ -0,0 +1,150 @@ +/* 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 { createRootLogger, Logger } from '@ironfish/sdk' +import { AccountImport } from '@ironfish/sdk/src/wallet/exporter' +import TransportNodeHid from '@ledgerhq/hw-transport-node-hid' +import IronfishApp, { + IronfishKeys, + ResponseAddress, + ResponseProofGenKey, + ResponseSign, + ResponseViewKey, +} from '@zondax/ledger-ironfish' + +export class Ledger { + app: IronfishApp | undefined + logger: Logger + PATH = "m/44'/1338'/0" + + constructor(logger?: Logger) { + this.app = undefined + this.logger = logger ? logger : createRootLogger() + } + + connect = async () => { + const transport = await TransportNodeHid.create(3000, 3000) + + const app = new IronfishApp(transport) + + const appInfo = await app.appInfo() + this.logger.debug(appInfo.appName ?? 'no app name') + + if (appInfo.appName !== 'Ironfish') { + this.logger.debug(appInfo.appName ?? 'no app name') + this.logger.debug(appInfo.returnCode.toString()) + this.logger.debug(appInfo.errorMessage.toString()) + throw new Error('Please open the Iron Fish app on your ledger device') + } + + if (appInfo.appVersion) { + this.logger.debug(`Ironfish App Version: ${appInfo.appVersion}`) + } + + this.app = app + + return { app, PATH: this.PATH } + } + + getPublicAddress = async () => { + if (!this.app) { + throw new Error('Connect to Ledger first') + } + + const response: ResponseAddress = await this.app.retrieveKeys( + this.PATH, + IronfishKeys.PublicAddress, + false, + ) + + if (!response.publicAddress) { + this.logger.debug(`No public address returned.`) + this.logger.debug(response.returnCode.toString()) + throw new Error(response.errorMessage) + } + + return response.publicAddress.toString('hex') + } + + importAccount = async () => { + if (!this.app) { + throw new Error('Connect to Ledger first') + } + + const responseAddress: ResponseAddress = await this.app.retrieveKeys( + this.PATH, + IronfishKeys.PublicAddress, + false, + ) + + if (!responseAddress.publicAddress) { + this.logger.debug(`No public address returned.`) + this.logger.debug(responseAddress.returnCode.toString()) + throw new Error(responseAddress.errorMessage) + } + + this.logger.log('Please confirm the request on your ledger device.') + + const responseViewKey: ResponseViewKey = await this.app.retrieveKeys( + this.PATH, + IronfishKeys.ViewKey, + true, + ) + + if (!responseViewKey.viewKey || !responseViewKey.ovk || !responseViewKey.ivk) { + this.logger.debug(`No view key returned.`) + this.logger.debug(responseViewKey.returnCode.toString()) + throw new Error(responseViewKey.errorMessage) + } + + const responsePGK: ResponseProofGenKey = await this.app.retrieveKeys( + this.PATH, + IronfishKeys.ProofGenerationKey, + false, + ) + + if (!responsePGK.ak || !responsePGK.nsk) { + this.logger.debug(`No proof authorizing key returned.`) + throw new Error(responsePGK.errorMessage) + } + + const accountImport: AccountImport = { + version: 4, // ACCOUNT_SCHEMA_VERSION as of 2024-05 + name: 'ledger', + viewKey: responseViewKey.viewKey.toString('hex'), + incomingViewKey: responseViewKey.ivk.toString('hex'), + outgoingViewKey: responseViewKey.ovk.toString('hex'), + publicAddress: responseAddress.publicAddress.toString('hex'), + proofAuthorizingKey: responsePGK.nsk.toString('hex'), + spendingKey: null, + createdAt: null, + } + + return accountImport + } + + sign = async (message: string): Promise => { + if (!this.app) { + throw new Error('Connect to Ledger first') + } + + this.logger.log('Please confirm the request on your ledger device.') + + const buffer = Buffer.from(message, 'hex') + + // max size of a transaction is 16kb + if (buffer.length > 16 * 1024) { + throw new Error('Transaction size is too large, must be less than 16kb.') + } + + const response: ResponseSign = await this.app.sign(this.PATH, buffer) + + if (!response.signature) { + this.logger.debug(`No signatures returned.`) + this.logger.debug(response.returnCode.toString()) + throw new Error(response.errorMessage) + } + + return response.signature + } +} diff --git a/yarn.lock b/yarn.lock index 7a7ecb601f..92865334a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -809,6 +809,70 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@ledgerhq/devices@^8.0.0", "@ledgerhq/devices@^8.3.0", "@ledgerhq/devices@^8.4.0": + version "8.4.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.4.0.tgz#f3a03576d4a53d731bdaa212a00bd0adbfb86fb1" + integrity sha512-TUrMlWZJ+5AFp2lWMw4rGQoU+WtjIqlFX5SzQDL9phaUHrt4TFierAGHsaj5+tUHudhD4JhIaLI2cn1NOyq5NQ== + dependencies: + "@ledgerhq/errors" "^6.17.0" + "@ledgerhq/logs" "^6.12.0" + rxjs "^7.8.1" + semver "^7.3.5" + +"@ledgerhq/errors@^6.12.3", "@ledgerhq/errors@^6.16.4", "@ledgerhq/errors@^6.17.0": + version "6.17.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.17.0.tgz#0d56361fe6eb7de3b239e661710679f933f1fcca" + integrity sha512-xnOVpy/gUUkusEORdr2Qhw3Vd0MGfjyVGgkGR9Ck6FXE26OIdIQ3tNmG5BdZN+gwMMFJJVxxS4/hr0taQfZ43w== + +"@ledgerhq/hw-transport-node-hid-noevents@^6.29.6": + version "6.30.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-6.30.1.tgz#e84854c809dda02bcb74a6d3dcc20b6014b5210d" + integrity sha512-9Mb5vDBXfSaRhfl0U2DnJLN4FgosfQopkzjzZYYHT3+s9XMot4WN/eWWbv5Ksx5qsV8RLQ77dewFFomNthm/vQ== + dependencies: + "@ledgerhq/devices" "^8.4.0" + "@ledgerhq/errors" "^6.17.0" + "@ledgerhq/hw-transport" "^6.31.0" + "@ledgerhq/logs" "^6.12.0" + node-hid "2.1.2" + +"@ledgerhq/hw-transport-node-hid@6.28.6": + version "6.28.6" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-6.28.6.tgz#8a62d1aaf4d62c53639e42171078a082d7cdda50" + integrity sha512-USSTOO0zv9XtguWismP7/StnNS/s7Rz0JOGGaBhKe3Bzl7d5XPncUlmOvoNFzzY/QdasEoFs2QId1+ibJG71Vw== + dependencies: + "@ledgerhq/devices" "^8.3.0" + "@ledgerhq/errors" "^6.16.4" + "@ledgerhq/hw-transport" "^6.30.6" + "@ledgerhq/hw-transport-node-hid-noevents" "^6.29.6" + "@ledgerhq/logs" "^6.12.0" + lodash "^4.17.21" + node-hid "^2.1.2" + usb "2.9.0" + +"@ledgerhq/hw-transport@6.28.1": + version "6.28.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.28.1.tgz#cb22fe9bc23af4682c30f2aac7fe6f7ab13ed65a" + integrity sha512-RaZe+abn0zBIz82cE9tp7Y7aZkHWWbEaE2yJpfxT8AhFz3fx+BU0kLYzuRN9fmA7vKueNJ1MTVUCY+Ex9/CHSQ== + dependencies: + "@ledgerhq/devices" "^8.0.0" + "@ledgerhq/errors" "^6.12.3" + events "^3.3.0" + +"@ledgerhq/hw-transport@^6.30.6", "@ledgerhq/hw-transport@^6.31.0": + version "6.31.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.31.0.tgz#82d8154bbcec8dc0104009a646159190fba5ae76" + integrity sha512-BY1poLk8vlJdIYngp8Zfaa/V9n14dqgt1G7iNetVRhJVFEKp9EYONeC3x6q/N7x81LUpzBk6M+T+s46Z4UiXHw== + dependencies: + "@ledgerhq/devices" "^8.4.0" + "@ledgerhq/errors" "^6.17.0" + "@ledgerhq/logs" "^6.12.0" + events "^3.3.0" + +"@ledgerhq/logs@^6.12.0": + version "6.12.0" + resolved "https://registry.yarnpkg.com/@ledgerhq/logs/-/logs-6.12.0.tgz#ad903528bf3687a44da435d7b2479d724d374f5d" + integrity sha512-ExDoj1QV5eC6TEbMdLUMMk9cfvNKhhv5gXol4SmULRVCx/3iyCPhJ74nsb3S0Vb+/f+XujBEj3vQn5+cwS0fNA== + "@lerna/add@6.4.1": version "6.4.1" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-6.4.1.tgz#fa20fe9ff875dc5758141262c8cde0d9a6481ec4" @@ -2884,6 +2948,11 @@ "@types/expect" "^1.20.4" "@types/node" "*" +"@types/w3c-web-usb@^1.0.6": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@types/w3c-web-usb/-/w3c-web-usb-1.0.10.tgz#cf89cccd2d93b6245e784c19afe0a9f5038d4528" + integrity sha512-CHgUI5kTc/QLMP8hODUHhge0D4vx+9UiAwIGiT0sTy/B2XpdX1U5rJt6JSISgr6ikRT7vxV9EVAFeYZqUnl1gQ== + "@types/ws@8.5.4": version "8.5.4" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" @@ -3160,6 +3229,20 @@ dependencies: argparse "^2.0.1" +"@zondax/ledger-ironfish@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@zondax/ledger-ironfish/-/ledger-ironfish-0.1.2.tgz#2ff93139c706734eb0d6800f743a9e0c2ae5268d" + integrity sha512-a9qnSOHxAf76pMonJBy5jI9oauR2W7WpVu/cCBs151uEW78NeSu4IMHOLGCo8KNiTPzpGwGa/7+1bpzxlQiEng== + dependencies: + "@zondax/ledger-js" "^0.2.1" + +"@zondax/ledger-js@^0.2.1": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@zondax/ledger-js/-/ledger-js-0.2.2.tgz#b334cecaa372a8bfb91ae4fc5dd0d1c52411da4e" + integrity sha512-7wOUlRF2+kRaRU2KSzKb7XjPfScwEg3Cjg6NH/p+ikQLJ9eMkGC45NhSxYn8lixIIk+TgZ4yzTNOzFvF836gQw== + dependencies: + "@ledgerhq/hw-transport" "6.28.1" + JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -3600,6 +3683,13 @@ binaryextensions@^4.15.0, binaryextensions@^4.16.0: resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-4.18.0.tgz#22aeada2d14de062c60e8ca59a504a5636a76ceb" integrity sha512-PQu3Kyv9dM4FnwB7XGj1+HucW+ShvJzJqjuw1JkKVs1mWdwOKVcRjOi+pV9X52A0tNvrPCsPkbFFQb+wE1EAXw== +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + bl@^4.0.3, bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -5284,6 +5374,11 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + filelist@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b" @@ -7927,7 +8022,7 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== -node-addon-api@^3.2.1: +node-addon-api@^3.0.2, node-addon-api@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== @@ -7937,6 +8032,11 @@ node-addon-api@^4.2.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== +node-addon-api@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + node-cleanup@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/node-cleanup/-/node-cleanup-2.1.2.tgz#7ac19abd297e09a7f72a71545d951b517e4dde2c" @@ -7975,7 +8075,7 @@ node-forge@1.3.1: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== -node-gyp-build@^4.2.0: +node-gyp-build@^4.2.0, node-gyp-build@^4.5.0: version "4.8.1" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.1.tgz#976d3ad905e71b76086f4f0b0d3637fe79b6cda5" integrity sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== @@ -8017,6 +8117,24 @@ node-gyp@^9.0.0: tar "^6.1.2" which "^2.0.2" +node-hid@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-2.1.2.tgz#3145fa86ed4336a402a71e9f372c54213b88797c" + integrity sha512-qhCyQqrPpP93F/6Wc/xUR7L8mAJW0Z6R7HMQV8jCHHksAxNDe/4z4Un/H9CpLOT+5K39OPyt9tIQlavxWES3lg== + dependencies: + bindings "^1.5.0" + node-addon-api "^3.0.2" + prebuild-install "^7.1.1" + +node-hid@^2.1.2: + version "2.2.0" + resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-2.2.0.tgz#33e039e7530a7bfe2b7a25f0a2f9496af8b02236" + integrity sha512-vj48zh9j555DZzUhMc8tk/qw6xPFrDyPBH1ST1Z/hWaA/juBJw7IuSxPeOgpzNFNU36mGYj+THioRMt1xOdm/g== + dependencies: + bindings "^1.5.0" + node-addon-api "^3.0.2" + prebuild-install "^7.1.1" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -8798,6 +8916,24 @@ prebuild-install@^7.0.1: tar-fs "^2.0.0" tunnel-agent "^0.6.0" +prebuild-install@^7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.2.tgz#a5fd9986f5a6251fbc47e1e5c65de71e68c0a056" + integrity sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ== + dependencies: + detect-libc "^2.0.0" + expand-template "^2.0.3" + github-from-package "0.0.0" + minimist "^1.2.3" + mkdirp-classic "^0.5.3" + napi-build-utils "^1.0.1" + node-abi "^3.3.0" + pump "^3.0.0" + rc "^1.2.7" + simple-get "^4.0.0" + tar-fs "^2.0.0" + tunnel-agent "^0.6.0" + preferred-pm@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/preferred-pm/-/preferred-pm-3.0.3.tgz#1b6338000371e3edbce52ef2e4f65eb2e73586d6" @@ -9271,7 +9407,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^7.0.0: +rxjs@^7.0.0, rxjs@^7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== @@ -10325,6 +10461,15 @@ urlgrey@1.0.0: dependencies: fast-url-parser "^1.1.3" +usb@2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/usb/-/usb-2.9.0.tgz#8ae3b175f93bee559400bff33491eee63406b6a2" + integrity sha512-G0I/fPgfHUzWH8xo2KkDxTTFruUWfppgSFJ+bQxz/kVY2x15EQ/XDB7dqD1G432G4gBG4jYQuF3U7j/orSs5nw== + dependencies: + "@types/w3c-web-usb" "^1.0.6" + node-addon-api "^6.0.0" + node-gyp-build "^4.5.0" + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" From 2bb8ed2abdaf7941e312f1247d3cff1e0dd6d4be Mon Sep 17 00:00:00 2001 From: andrea Date: Fri, 28 Jun 2024 13:00:47 -0700 Subject: [PATCH 22/81] Prevent race conditions when getting list of accounts during scanning Calling `Wallet.reset()` while a scanning is running can sometimes result in race conditions. In particular, `WalletScanner` may attempt to query account heads at the same time while accounts are being reset, resulting in errors. This commit fixes the problem by using database transactions (1) while `WalletScanner` read accounts and (2) while `Wallet.reset()` updates accounts. This commit also adds transactions in `Wallet.getEarliestHead()` and `Wallet.getLatestHead()`. These methods are not used during scanning, but they suffer from the same kind of problem. Note that this commit fixes specific race conditions related to getting the list of accounts to scan, but other race conditions that affect scanning may still exist. --- ironfish/src/wallet/scanner/walletScanner.ts | 43 ++++++------ ironfish/src/wallet/wallet.ts | 71 +++++++++++--------- 2 files changed, 62 insertions(+), 52 deletions(-) diff --git a/ironfish/src/wallet/scanner/walletScanner.ts b/ironfish/src/wallet/scanner/walletScanner.ts index b0a3b67472..860dd4a48e 100644 --- a/ironfish/src/wallet/scanner/walletScanner.ts +++ b/ironfish/src/wallet/scanner/walletScanner.ts @@ -10,7 +10,7 @@ import { Config } from '../../fileStores' import { Logger } from '../../logger' import { Mutex } from '../../mutex' import { BlockHeader, Transaction } from '../../primitives' -import { AsyncUtils, BufferUtils, HashUtils } from '../../utils' +import { BufferUtils, HashUtils } from '../../utils' import { WorkerPool } from '../../workerPool' import { ChainProcessorWithTransactions } from './chainProcessorWithTransactions' import { BackgroundNoteDecryptor } from './noteDecryptor' @@ -224,21 +224,14 @@ export class WalletScanner { ): Promise { this.logger.debug(`AccountHead DEL: ${header.sequence} => ${Number(header.sequence) - 1}`) - const accounts = await AsyncUtils.filter(this.wallet.listAccounts(), async (account) => { - if (!account.scanningEnabled) { - return false - } - - const accountHead = await account.getHead() - - return BufferUtils.equalsNullable(accountHead?.hash ?? null, header.hash) - }) + const accounts = (await this.getScanningAccountsWithHead()).filter(({ head }) => + BufferUtils.equalsNullable(head?.hash, header.hash), + ) - for (const account of accounts) { + for (const { account } of accounts) { if (abort?.signal.aborted) { return } - await this.wallet.disconnectBlockForAccount(account, header, transactions) } } @@ -285,18 +278,28 @@ export class WalletScanner { ) } + private getScanningAccountsWithHead(): Promise< + Array<{ account: Account; head: HeadValue | null }> + > { + return this.wallet.walletDb.db.withTransaction(null, async (tx) => + Promise.all( + this.wallet + .listAccounts() + .filter((account) => account.scanningEnabled) + .map(async (account) => ({ + account, + head: await account.getHead(tx), + })), + ), + ) + } + /** * Replaces `scanningAccounts` with fresh values from the wallet. */ private async refreshScanningAccounts(): Promise { - this.scanningAccounts = await Promise.all( - this.wallet - .listAccounts() - .filter((account) => account.scanningEnabled) - .map(async (account) => ({ - account, - scanFrom: await account.getHead(), - })), + this.scanningAccounts = (await this.getScanningAccountsWithHead()).map( + ({ account, head }) => ({ account, scanFrom: head }), ) } diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index 4184076fcb..132f010ce2 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.ts @@ -329,14 +329,17 @@ export class Wallet { await this.resetAccounts(options) } - async resetAccounts(options?: { + resetAccounts(options?: { resetCreatedAt?: boolean resetScanningEnabled?: boolean tx?: IDatabaseTransaction }): Promise { - for (const account of this.listAccounts()) { - await this.resetAccount(account, options) - } + return this.walletDb.db.withTransaction(options?.tx, async (tx) => { + const txOptions = { ...options, tx } + for (const account of this.listAccounts()) { + await this.resetAccount(account, txOptions) + } + }) } async decryptNotes( @@ -1574,47 +1577,51 @@ export class Wallet { return this.getAccount(this.defaultAccount) } - async getEarliestHead(): Promise { - let earliestHead = null - for (const account of this.accounts.values()) { - if (!account.scanningEnabled) { - continue - } + getEarliestHead(tx?: IDatabaseTransaction): Promise { + return this.walletDb.db.withTransaction(tx, async (tx) => { + let earliestHead = null + for (const account of this.accounts.values()) { + if (!account.scanningEnabled) { + continue + } - const head = await account.getHead() + const head = await account.getHead(tx) - if (!head) { - return null - } + if (!head) { + return null + } - if (!earliestHead || earliestHead.sequence > head.sequence) { - earliestHead = head + if (!earliestHead || earliestHead.sequence > head.sequence) { + earliestHead = head + } } - } - return earliestHead + return earliestHead + }) } - async getLatestHead(): Promise { - let latestHead = null + getLatestHead(tx?: IDatabaseTransaction): Promise { + return this.walletDb.db.withTransaction(tx, async (tx) => { + let latestHead = null - for (const account of this.accounts.values()) { - if (!account.scanningEnabled) { - continue - } + for (const account of this.accounts.values()) { + if (!account.scanningEnabled) { + continue + } - const head = await account.getHead() + const head = await account.getHead(tx) - if (!head) { - continue - } + if (!head) { + continue + } - if (!latestHead || latestHead.sequence < head.sequence) { - latestHead = head + if (!latestHead || latestHead.sequence < head.sequence) { + latestHead = head + } } - } - return latestHead + return latestHead + }) } async isAccountUpToDate(account: Account, confirmations?: number): Promise { From d24f751f3b767d305cb8d87ad19c44b250e90c75 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Tue, 2 Jul 2024 15:58:52 -0400 Subject: [PATCH 23/81] Add test for ArrayUtils (#5102) * Add test for ArrayUtils * Improve shuffle test * Improve sample test --- ironfish/src/utils/array.test.ts | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 ironfish/src/utils/array.test.ts diff --git a/ironfish/src/utils/array.test.ts b/ironfish/src/utils/array.test.ts new file mode 100644 index 0000000000..032a4c3c58 --- /dev/null +++ b/ironfish/src/utils/array.test.ts @@ -0,0 +1,50 @@ +/* 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 } from '../assert' +import { ArrayUtils } from './array' + +describe('ArrayUtils', () => { + it('shuffles array', () => { + const items: number[] = [] + for (let i = 0; i <= 10000; ++i) { + items.push(i) + } + + const shuffled = ArrayUtils.shuffle(items) + expect(shuffled).not.toEqual(items) + expect(shuffled.sort((a, b) => a - b)).toEqual(items) + }) + + it('sample a random item', () => { + // single element + expect(ArrayUtils.sample([2])).toBe(2) + + // empty array + expect(ArrayUtils.sample([])).toBeNull() + + // test randomness + const samples = [0, 1, 2] + const found = new Set(samples) + + for (let i = 0; i < 10000; ++i) { + const sample = ArrayUtils.sample(samples) + Assert.isNotNull(sample) + found.delete(sample) + } + + expect(found.size).toBe(0) + }) + + it('removes an item in places', () => { + const items = [0, 1, 2] + + let removed = ArrayUtils.remove(items, 1) + expect(removed).toBe(true) + expect(items).toEqual([0, 2]) + + removed = ArrayUtils.remove(items, 100) + expect(removed).toBe(false) + expect(items).toEqual([0, 2]) + }) +}) From 73fc1e09ea2c9057f0769edde25dbc1c6479f660 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Wed, 3 Jul 2024 12:25:23 -0400 Subject: [PATCH 24/81] Move transactions outside of options (#5104) --- ironfish/src/wallet/wallet.ts | 39 +++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index 132f010ce2..4cc0ccbcd9 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.ts @@ -321,23 +321,26 @@ export class Wallet { } } - async reset(options?: { - resetCreatedAt?: boolean - resetScanningEnabled?: boolean - tx?: IDatabaseTransaction - }): Promise { - await this.resetAccounts(options) - } - - resetAccounts(options?: { - resetCreatedAt?: boolean - resetScanningEnabled?: boolean - tx?: IDatabaseTransaction - }): Promise { - return this.walletDb.db.withTransaction(options?.tx, async (tx) => { - const txOptions = { ...options, tx } + async reset( + options?: { + resetCreatedAt?: boolean + resetScanningEnabled?: boolean + }, + tx?: IDatabaseTransaction, + ): Promise { + await this.resetAccounts(options, tx) + } + + resetAccounts( + options?: { + resetCreatedAt?: boolean + resetScanningEnabled?: boolean + }, + tx?: IDatabaseTransaction, + ): Promise { + return this.walletDb.db.withTransaction(tx, async (tx) => { for (const account of this.listAccounts()) { - await this.resetAccount(account, txOptions) + await this.resetAccount(account, options, tx) } }) } @@ -1424,8 +1427,8 @@ export class Wallet { options?: { resetCreatedAt?: boolean resetScanningEnabled?: boolean - tx?: IDatabaseTransaction }, + tx?: IDatabaseTransaction, ): Promise { const newAccount = new Account({ accountValue: { @@ -1439,7 +1442,7 @@ export class Wallet { this.logger.debug(`Resetting account name: ${account.name}, id: ${account.id}`) - await this.walletDb.db.withTransaction(options?.tx, async (tx) => { + await this.walletDb.db.withTransaction(tx, async (tx) => { await this.walletDb.setAccount(newAccount, tx) if (newAccount.createdAt !== null) { From 50053b021d98bb86c0b1fdc9ce040b1520a0e5ee Mon Sep 17 00:00:00 2001 From: Hugh Cunningham <57735705+hughy@users.noreply.github.com> Date: Wed, 3 Jul 2024 09:57:02 -0700 Subject: [PATCH 25/81] stops scanState meter on scan completion (#5108) when started the Meter class sets an interval to update its underlying arrays. if the meter is not stopped then this interval will keep running and keep the meter in the heap and leaks the meter memory. the WalletScanner creates a new ScanState and a new Meter for each scan. when the wallet is at the head of the chain this creates a new scan each time a block is added. fixes the memory leak by ensuring that the ScanState Meter is stopped when the scan completes and its interval function is cleared --- ironfish/src/wallet/scanner/scanState.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ironfish/src/wallet/scanner/scanState.ts b/ironfish/src/wallet/scanner/scanState.ts index 2d4864ec84..eda13da0a5 100644 --- a/ironfish/src/wallet/scanner/scanState.ts +++ b/ironfish/src/wallet/scanner/scanState.ts @@ -51,6 +51,7 @@ export class ScanState { } signalComplete(): void { + this.speed.stop() this.runningResolve() } From 1ca1922192871d68c0e3712b581d74d39775b6fc Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Wed, 3 Jul 2024 10:03:57 -0700 Subject: [PATCH 26/81] update to latest ledger hq version (#5107) --- ironfish-cli/package.json | 2 +- yarn.lock | 35 +++++++++++++---------------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/ironfish-cli/package.json b/ironfish-cli/package.json index b15b7a6319..480d387aab 100644 --- a/ironfish-cli/package.json +++ b/ironfish-cli/package.json @@ -60,7 +60,7 @@ "dependencies": { "@ironfish/rust-nodejs": "2.4.0", "@ironfish/sdk": "2.4.0", - "@ledgerhq/hw-transport-node-hid": "6.28.6", + "@ledgerhq/hw-transport-node-hid": "6.29.1", "@oclif/core": "3.27.0", "@oclif/plugin-autocomplete": "1.3.10", "@oclif/plugin-help": "5.1.12", diff --git a/yarn.lock b/yarn.lock index 92865334a3..8092325436 100644 --- a/yarn.lock +++ b/yarn.lock @@ -809,7 +809,7 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@ledgerhq/devices@^8.0.0", "@ledgerhq/devices@^8.3.0", "@ledgerhq/devices@^8.4.0": +"@ledgerhq/devices@^8.0.0", "@ledgerhq/devices@^8.4.0": version "8.4.0" resolved "https://registry.yarnpkg.com/@ledgerhq/devices/-/devices-8.4.0.tgz#f3a03576d4a53d731bdaa212a00bd0adbfb86fb1" integrity sha512-TUrMlWZJ+5AFp2lWMw4rGQoU+WtjIqlFX5SzQDL9phaUHrt4TFierAGHsaj5+tUHudhD4JhIaLI2cn1NOyq5NQ== @@ -819,12 +819,12 @@ rxjs "^7.8.1" semver "^7.3.5" -"@ledgerhq/errors@^6.12.3", "@ledgerhq/errors@^6.16.4", "@ledgerhq/errors@^6.17.0": +"@ledgerhq/errors@^6.12.3", "@ledgerhq/errors@^6.17.0": version "6.17.0" resolved "https://registry.yarnpkg.com/@ledgerhq/errors/-/errors-6.17.0.tgz#0d56361fe6eb7de3b239e661710679f933f1fcca" integrity sha512-xnOVpy/gUUkusEORdr2Qhw3Vd0MGfjyVGgkGR9Ck6FXE26OIdIQ3tNmG5BdZN+gwMMFJJVxxS4/hr0taQfZ43w== -"@ledgerhq/hw-transport-node-hid-noevents@^6.29.6": +"@ledgerhq/hw-transport-node-hid-noevents@^6.30.1": version "6.30.1" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid-noevents/-/hw-transport-node-hid-noevents-6.30.1.tgz#e84854c809dda02bcb74a6d3dcc20b6014b5210d" integrity sha512-9Mb5vDBXfSaRhfl0U2DnJLN4FgosfQopkzjzZYYHT3+s9XMot4WN/eWWbv5Ksx5qsV8RLQ77dewFFomNthm/vQ== @@ -835,18 +835,18 @@ "@ledgerhq/logs" "^6.12.0" node-hid "2.1.2" -"@ledgerhq/hw-transport-node-hid@6.28.6": - version "6.28.6" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-6.28.6.tgz#8a62d1aaf4d62c53639e42171078a082d7cdda50" - integrity sha512-USSTOO0zv9XtguWismP7/StnNS/s7Rz0JOGGaBhKe3Bzl7d5XPncUlmOvoNFzzY/QdasEoFs2QId1+ibJG71Vw== +"@ledgerhq/hw-transport-node-hid@6.29.1": + version "6.29.1" + resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-node-hid/-/hw-transport-node-hid-6.29.1.tgz#03dbeee6d77f8bb73ec1ef06902436fd137e47a0" + integrity sha512-l+zAfsE0uvo2/Wni0TSW+n6HoFmZdPH6ukrjPocY6jvbhcaxhpbK7ERvDpnZMir/pHwsDoAsvwPY/0sFRBf7bw== dependencies: - "@ledgerhq/devices" "^8.3.0" - "@ledgerhq/errors" "^6.16.4" - "@ledgerhq/hw-transport" "^6.30.6" - "@ledgerhq/hw-transport-node-hid-noevents" "^6.29.6" + "@ledgerhq/devices" "^8.4.0" + "@ledgerhq/errors" "^6.17.0" + "@ledgerhq/hw-transport" "^6.31.0" + "@ledgerhq/hw-transport-node-hid-noevents" "^6.30.1" "@ledgerhq/logs" "^6.12.0" lodash "^4.17.21" - node-hid "^2.1.2" + node-hid "2.1.2" usb "2.9.0" "@ledgerhq/hw-transport@6.28.1": @@ -858,7 +858,7 @@ "@ledgerhq/errors" "^6.12.3" events "^3.3.0" -"@ledgerhq/hw-transport@^6.30.6", "@ledgerhq/hw-transport@^6.31.0": +"@ledgerhq/hw-transport@^6.31.0": version "6.31.0" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-6.31.0.tgz#82d8154bbcec8dc0104009a646159190fba5ae76" integrity sha512-BY1poLk8vlJdIYngp8Zfaa/V9n14dqgt1G7iNetVRhJVFEKp9EYONeC3x6q/N7x81LUpzBk6M+T+s46Z4UiXHw== @@ -8126,15 +8126,6 @@ node-hid@2.1.2: node-addon-api "^3.0.2" prebuild-install "^7.1.1" -node-hid@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/node-hid/-/node-hid-2.2.0.tgz#33e039e7530a7bfe2b7a25f0a2f9496af8b02236" - integrity sha512-vj48zh9j555DZzUhMc8tk/qw6xPFrDyPBH1ST1Z/hWaA/juBJw7IuSxPeOgpzNFNU36mGYj+THioRMt1xOdm/g== - dependencies: - bindings "^1.5.0" - node-addon-api "^3.0.2" - prebuild-install "^7.1.1" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" From be16edbe863d95bce8effb2b3f1aca7a16a6bb03 Mon Sep 17 00:00:00 2001 From: Hugh Cunningham <57735705+hughy@users.noreply.github.com> Date: Mon, 8 Jul 2024 09:02:46 -0700 Subject: [PATCH 27/81] ensures that postTransaction uses a spending account (#5092) replaces 'getAccountByPublicAddress' with a more generic 'findAccount' that takes a predicate uses findAccount in postTransaction to find the account that has the same public address and is a spending account fixes issue where wallet has a spending account and a view only account with the same public address and cannot post a transaction --- ironfish/src/rpc/routes/wallet/postTransaction.ts | 5 ++++- ironfish/src/wallet/wallet.ts | 14 +++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ironfish/src/rpc/routes/wallet/postTransaction.ts b/ironfish/src/rpc/routes/wallet/postTransaction.ts index f763a86c28..326b41792b 100644 --- a/ironfish/src/rpc/routes/wallet/postTransaction.ts +++ b/ironfish/src/rpc/routes/wallet/postTransaction.ts @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import * as yup from 'yup' import { RawTransactionSerde } from '../../../primitives/rawTransaction' +import { Account } from '../../../wallet' import { RpcValidationError } from '../../adapters' import { ApiNamespace } from '../namespaces' import { routes } from '../router' @@ -55,7 +56,9 @@ routes.register( throw new RpcValidationError('Unable to determine sender account for raw transaction') } - const account = context.wallet.getAccountByPublicAddress(sender) + const account = context.wallet.findAccount( + (account: Account) => account.publicAddress === sender && account.isSpendingAccount(), + ) if (account === null) { throw new RpcValidationError( diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index 4cc0ccbcd9..ced8b5e809 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.ts @@ -1544,22 +1544,18 @@ export class Wallet { this.defaultAccount = nextId } - getAccountByName(name: string): Account | null { + findAccount(predicate: (account: Account) => boolean): Account | null { for (const account of this.accounts.values()) { - if (name === account.name) { + if (predicate(account)) { return account } } + return null } - getAccountByPublicAddress(publicAddress: string): Account | null { - for (const account of this.accounts.values()) { - if (publicAddress === account.publicAddress) { - return account - } - } - return null + getAccountByName(name: string): Account | null { + return this.findAccount((account) => account.name === name) } getAccount(id: string): Account | null { From a8c8c51cafa5e358f6b98d8768f28a1618fc5230 Mon Sep 17 00:00:00 2001 From: andrea Date: Mon, 8 Jul 2024 09:51:15 -0700 Subject: [PATCH 28/81] Fix a style warning from Docker ``` WARN: FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 1) ``` --- ironfish-cli/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironfish-cli/Dockerfile b/ironfish-cli/Dockerfile index 7f49291dd0..a7e3336a02 100644 --- a/ironfish-cli/Dockerfile +++ b/ironfish-cli/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20-bookworm as build +FROM node:20-bookworm AS build ENV PATH="/root/.cargo/bin:${PATH}" RUN \ From 2c1b273754bd7b394f7add7f8d2b4d7c1e0897e4 Mon Sep 17 00:00:00 2001 From: andrea Date: Mon, 8 Jul 2024 09:51:45 -0700 Subject: [PATCH 29/81] Include the git commit hash in Docker images Send to the Docker build daemon the necessary files from the `.git` directory so that the build scripts can extract the hash of the HEAD commit. --- ironfish-cli/scripts/build-docker.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ironfish-cli/scripts/build-docker.sh b/ironfish-cli/scripts/build-docker.sh index 803cf0f8c3..bc59dce58d 100755 --- a/ironfish-cli/scripts/build-docker.sh +++ b/ironfish-cli/scripts/build-docker.sh @@ -3,8 +3,14 @@ set -euo pipefail cd "$(dirname "$0")/../.." cat .gitignore - > .dockerignore < Date: Mon, 8 Jul 2024 16:59:53 -0400 Subject: [PATCH 30/81] Import duplicate spending key says account name (#5115) When importing an account twice, it would not print out the name of the account with the duplicate spending key. This will now include that in the message. It also improves tests to check the error type instead of error message. We should avoid testing error messages in tests if there is a proper typed error to check. --- ironfish/src/wallet/errors.ts | 9 +++++++++ ironfish/src/wallet/wallet.test.ts | 12 +++++++----- ironfish/src/wallet/wallet.ts | 15 +++++++++------ 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/ironfish/src/wallet/errors.ts b/ironfish/src/wallet/errors.ts index 344c0b2690..342b79c235 100644 --- a/ironfish/src/wallet/errors.ts +++ b/ironfish/src/wallet/errors.ts @@ -43,6 +43,15 @@ export class DuplicateAccountNameError extends Error { } } +export class DuplicateSpendingKeyError extends Error { + name = this.constructor.name + + constructor(name: string) { + super() + this.message = `Account already exists with provided spending key: ${name}` + } +} + export class DuplicateMultisigSecretNameError extends Error { name = this.constructor.name diff --git a/ironfish/src/wallet/wallet.test.ts b/ironfish/src/wallet/wallet.test.ts index 86079dcd63..79202b7135 100644 --- a/ironfish/src/wallet/wallet.test.ts +++ b/ironfish/src/wallet/wallet.test.ts @@ -22,7 +22,11 @@ import { } from '../testUtilities' import { AsyncUtils, BufferUtils, ORE_TO_IRON } from '../utils' import { Account, TransactionStatus, TransactionType } from '../wallet' -import { MaxMemoLengthError } from './errors' +import { + DuplicateAccountNameError, + DuplicateSpendingKeyError, + MaxMemoLengthError, +} from './errors' import { toAccountImport } from './exporter' import { AssetStatus, Wallet } from './wallet' @@ -499,7 +503,7 @@ describe('Wallet', () => { expect(node.wallet.accountExists(account.name)).toEqual(true) await expect(node.wallet.importAccount(account)).rejects.toThrow( - 'Account already exists with the name', + DuplicateAccountNameError, ) }) @@ -513,9 +517,7 @@ describe('Wallet', () => { const clone = { ...account } clone.name = 'Different name' - await expect(node.wallet.importAccount(clone)).rejects.toThrow( - 'Account already exists with provided spending key', - ) + await expect(node.wallet.importAccount(clone)).rejects.toThrow(DuplicateSpendingKeyError) }) it('should be able to import an account from solely its view keys', async () => { diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index ced8b5e809..d59a68da29 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.ts @@ -46,6 +46,7 @@ import { AssetBalances } from './assetBalances' import { DuplicateAccountNameError, DuplicateMultisigSecretNameError, + DuplicateSpendingKeyError, MaxMemoLengthError, NotEnoughFundsError, } from './errors' @@ -1348,12 +1349,14 @@ export class Wallet { throw new DuplicateAccountNameError(name) } - const accounts = this.listAccounts() - if ( - accountValue.spendingKey && - accounts.find((a) => accountValue.spendingKey === a.spendingKey) - ) { - throw new Error(`Account already exists with provided spending key`) + if (accountValue.spendingKey) { + const duplicateSpendingAccount = this.listAccounts().find( + (a) => accountValue.spendingKey === a.spendingKey, + ) + + if (duplicateSpendingAccount) { + throw new DuplicateSpendingKeyError(duplicateSpendingAccount.name) + } } validateAccountImport(accountValue) From fbe79db62670aaebd0400d33a742db8ae66dbbcd Mon Sep 17 00:00:00 2001 From: Hugh Cunningham <57735705+hughy@users.noreply.github.com> Date: Mon, 8 Jul 2024 14:40:52 -0700 Subject: [PATCH 31/81] version bump for v2.4.1 (#5117) --- ironfish-cli/package.json | 4 ++-- ironfish/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ironfish-cli/package.json b/ironfish-cli/package.json index 480d387aab..69fe699850 100644 --- a/ironfish-cli/package.json +++ b/ironfish-cli/package.json @@ -1,6 +1,6 @@ { "name": "ironfish", - "version": "2.4.0", + "version": "2.4.1", "description": "CLI for running and interacting with an Iron Fish node", "author": "Iron Fish (https://ironfish.network)", "main": "build/src/index.js", @@ -59,7 +59,7 @@ }, "dependencies": { "@ironfish/rust-nodejs": "2.4.0", - "@ironfish/sdk": "2.4.0", + "@ironfish/sdk": "2.4.1", "@ledgerhq/hw-transport-node-hid": "6.29.1", "@oclif/core": "3.27.0", "@oclif/plugin-autocomplete": "1.3.10", diff --git a/ironfish/package.json b/ironfish/package.json index 093f72d438..ac067139f2 100644 --- a/ironfish/package.json +++ b/ironfish/package.json @@ -1,6 +1,6 @@ { "name": "@ironfish/sdk", - "version": "2.4.0", + "version": "2.4.1", "description": "SDK for running and interacting with an Iron Fish node", "author": "Iron Fish (https://ironfish.network)", "main": "build/src/index.js", From c80830ab727073d4b47c4273f6149798ecb725bd Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Tue, 9 Jul 2024 10:19:50 -0700 Subject: [PATCH 32/81] wallet sign command and rpc (#5111) --- ironfish-cli/src/commands/wallet/sign.ts | 131 ++++++++++++++++++ ironfish-rust-nodejs/index.d.ts | 2 + ironfish-rust-nodejs/index.js | 3 +- ironfish-rust-nodejs/src/lib.rs | 19 +++ .../src/structs/transaction.rs | 6 + ironfish-rust/src/keys/mod.rs | 2 + ironfish-rust/src/keys/util.rs | 19 +++ ironfish-rust/src/transaction/unsigned.rs | 4 + ironfish/src/rpc/clients/client.ts | 14 +- .../src/rpc/routes/wallet/addSignature.ts | 19 ++- ironfish/src/rpc/routes/wallet/index.ts | 1 + .../src/rpc/routes/wallet/signTransaction.ts | 66 +++++++++ 12 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 ironfish-cli/src/commands/wallet/sign.ts create mode 100644 ironfish-rust/src/keys/util.rs create mode 100644 ironfish/src/rpc/routes/wallet/signTransaction.ts diff --git a/ironfish-cli/src/commands/wallet/sign.ts b/ironfish-cli/src/commands/wallet/sign.ts new file mode 100644 index 0000000000..420fbbbc66 --- /dev/null +++ b/ironfish-cli/src/commands/wallet/sign.ts @@ -0,0 +1,131 @@ +/* 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 { CurrencyUtils, RpcClient, Transaction } from '@ironfish/sdk' +import { Flags } from '@oclif/core' +import { IronfishCommand } from '../../command' +import { RemoteFlags } from '../../flags' +import { longPrompt } from '../../utils/input' +import { Ledger } from '../../utils/ledger' +import { watchTransaction } from '../../utils/transaction' + +export class SignTransaction extends IronfishCommand { + static description = `Sign an unsigned transaction` + static flags = { + ...RemoteFlags, + unsignedTransaction: Flags.string({ + char: 'u', + description: 'Unsigned transaction to sign.', + }), + ledger: Flags.boolean({ + description: 'Sign with a ledger device', + default: false, + }), + broadcast: Flags.boolean({ + default: false, + description: 'Broadcast the transaction to the network after signing', + }), + watch: Flags.boolean({ + default: false, + description: 'Wait for the transaction to be confirmed', + dependsOn: ['broadcast'], + }), + } + + async start(): Promise { + const { flags } = await this.parse(SignTransaction) + const client = await this.sdk.connectRpc() + + if (!flags.broadcast && flags.watch) { + this.error('Cannot use --watch without --broadcast') + } + + let unsignedTransaction = flags.unsignedTransaction + if (!unsignedTransaction) { + unsignedTransaction = await longPrompt('Enter the unsigned transaction', { + required: true, + }) + } + + let signedTransaction: string + let account: string + + if (flags.ledger) { + const response = await this.signWithLedger(client, unsignedTransaction) + signedTransaction = response.transaction + account = response.account + } else { + const response = await this.signWithAccount(client, unsignedTransaction) + signedTransaction = response.transaction + account = response.account + } + + const response = await client.wallet.addTransaction({ + transaction: signedTransaction, + broadcast: flags.broadcast, + }) + + const bytes = Buffer.from(signedTransaction, 'hex') + const transaction = new Transaction(bytes) + + this.log(`\nSigned Transaction: ${signedTransaction}`) + this.log(`\nHash: ${transaction.hash().toString('hex')}`) + this.log(`Fee: ${CurrencyUtils.render(transaction.fee(), true)}`) + + if (flags.broadcast && response.content.accepted === false) { + this.error( + `Transaction '${transaction.hash().toString('hex')}' was not accepted into the mempool`, + ) + } + + if (flags.watch) { + this.log('') + + await watchTransaction({ + client, + logger: this.logger, + account: account, + hash: transaction.hash().toString('hex'), + }) + } + } + + private async signWithAccount(client: RpcClient, unsignedTransaction: string) { + const response = await client.wallet.signTransaction({ + unsignedTransaction: unsignedTransaction, + }) + + return { + transaction: response.content.transaction, + account: response.content.account, + } + } + + private async signWithLedger(client: RpcClient, unsignedTransaction: string) { + const ledger = new Ledger(this.logger) + try { + await ledger.connect() + } catch (e) { + if (e instanceof Error) { + this.error(e.message) + } else { + throw e + } + } + + const signature = (await ledger.sign(unsignedTransaction)).toString('hex') + + this.log(`\nSignature: ${signature}`) + + const addSignatureResponse = await client.wallet.addSignature({ + unsignedTransaction, + signature, + }) + + return { + transaction: addSignatureResponse.content.transaction, + account: addSignatureResponse.content.account, + } + } +} diff --git a/ironfish-rust-nodejs/index.d.ts b/ironfish-rust-nodejs/index.d.ts index 886bf0e06d..7af18418a0 100644 --- a/ironfish-rust-nodejs/index.d.ts +++ b/ironfish-rust-nodejs/index.d.ts @@ -86,6 +86,7 @@ export function isValidPublicAddress(hexAddress: string): boolean * sandboxed environment. */ export function getCpuCount(): CpuCount +export function generateRandomizedPublicKey(viewKeyString: string, publicKeyRandomnessString: string): string export class FishHashContext { constructor(full: boolean) prebuildDataset(threads: number): void @@ -226,6 +227,7 @@ export type NativeUnsignedTransaction = UnsignedTransaction export class UnsignedTransaction { constructor(jsBytes: Buffer) serialize(): Buffer + randomizedPublicKey(): string publicKeyRandomness(): string hash(): Buffer signingPackage(nativeIdentiferCommitments: Array): string diff --git a/ironfish-rust-nodejs/index.js b/ironfish-rust-nodejs/index.js index 46ae24dd0c..6ca64090b1 100644 --- a/ironfish-rust-nodejs/index.js +++ b/ironfish-rust-nodejs/index.js @@ -252,7 +252,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { FishHashContext, KEY_LENGTH, NONCE_LENGTH, BoxKeyPair, randomBytes, boxMessage, unboxMessage, RollingFilter, initSignalHandler, triggerSegfault, ASSET_ID_LENGTH, ASSET_METADATA_LENGTH, ASSET_NAME_LENGTH, ASSET_LENGTH, Asset, NOTE_ENCRYPTION_KEY_LENGTH, MAC_LENGTH, ENCRYPTED_NOTE_PLAINTEXT_LENGTH, ENCRYPTED_NOTE_LENGTH, NoteEncrypted, PUBLIC_ADDRESS_LENGTH, RANDOMNESS_LENGTH, MEMO_LENGTH, AMOUNT_VALUE_LENGTH, DECRYPTED_NOTE_LENGTH, Note, PROOF_LENGTH, TRANSACTION_SIGNATURE_LENGTH, TRANSACTION_PUBLIC_KEY_RANDOMNESS_LENGTH, TRANSACTION_EXPIRATION_LENGTH, TRANSACTION_FEE_LENGTH, LATEST_TRANSACTION_VERSION, TransactionPosted, Transaction, verifyTransactions, UnsignedTransaction, LanguageCode, generateKey, spendingKeyToWords, wordsToSpendingKey, generatePublicAddressFromIncomingViewKey, generateKeyFromPrivateKey, initializeSapling, FoundBlockResult, ThreadPoolHandler, isValidPublicAddress, CpuCount, getCpuCount, multisig } = nativeBinding +const { FishHashContext, KEY_LENGTH, NONCE_LENGTH, BoxKeyPair, randomBytes, boxMessage, unboxMessage, RollingFilter, initSignalHandler, triggerSegfault, ASSET_ID_LENGTH, ASSET_METADATA_LENGTH, ASSET_NAME_LENGTH, ASSET_LENGTH, Asset, NOTE_ENCRYPTION_KEY_LENGTH, MAC_LENGTH, ENCRYPTED_NOTE_PLAINTEXT_LENGTH, ENCRYPTED_NOTE_LENGTH, NoteEncrypted, PUBLIC_ADDRESS_LENGTH, RANDOMNESS_LENGTH, MEMO_LENGTH, AMOUNT_VALUE_LENGTH, DECRYPTED_NOTE_LENGTH, Note, PROOF_LENGTH, TRANSACTION_SIGNATURE_LENGTH, TRANSACTION_PUBLIC_KEY_RANDOMNESS_LENGTH, TRANSACTION_EXPIRATION_LENGTH, TRANSACTION_FEE_LENGTH, LATEST_TRANSACTION_VERSION, TransactionPosted, Transaction, verifyTransactions, UnsignedTransaction, LanguageCode, generateKey, spendingKeyToWords, wordsToSpendingKey, generatePublicAddressFromIncomingViewKey, generateKeyFromPrivateKey, initializeSapling, FoundBlockResult, ThreadPoolHandler, isValidPublicAddress, CpuCount, getCpuCount, generateRandomizedPublicKey, multisig } = nativeBinding module.exports.FishHashContext = FishHashContext module.exports.KEY_LENGTH = KEY_LENGTH @@ -302,4 +302,5 @@ module.exports.ThreadPoolHandler = ThreadPoolHandler module.exports.isValidPublicAddress = isValidPublicAddress module.exports.CpuCount = CpuCount module.exports.getCpuCount = getCpuCount +module.exports.generateRandomizedPublicKey = generateRandomizedPublicKey module.exports.multisig = multisig diff --git a/ironfish-rust-nodejs/src/lib.rs b/ironfish-rust-nodejs/src/lib.rs index 60d2aa4f54..e21182f4f1 100644 --- a/ironfish-rust-nodejs/src/lib.rs +++ b/ironfish-rust-nodejs/src/lib.rs @@ -5,12 +5,15 @@ use std::fmt::Display; use std::num::NonZeroUsize; +use ironfish::keys::generate_randomized_public_key; use ironfish::keys::Language; use ironfish::serializing::bytes_to_hex; +use ironfish::serializing::fr::FrSerializable; use ironfish::IncomingViewKey; use ironfish::PublicAddress; use ironfish::SaplingKey; +use ironfish::ViewKey; use napi::bindgen_prelude::*; use napi_derive::napi; @@ -255,3 +258,19 @@ pub fn get_cpu_count() -> CpuCount { physical_count: num_cpus::get_physical() as u32, } } + +#[napi(js_name = "generateRandomizedPublicKey")] +pub fn randomize_pk( + view_key_string: String, + public_key_randomness_string: String, +) -> Result { + let view_key = ViewKey::from_hex(&view_key_string).map_err(to_napi_err)?; + + let public_key_randomness = + jubjub::Fr::from_hex(&public_key_randomness_string).map_err(to_napi_err)?; + + let public_key = + generate_randomized_public_key(view_key, public_key_randomness).map_err(to_napi_err)?; + + Ok(bytes_to_hex(&public_key)) +} diff --git a/ironfish-rust-nodejs/src/structs/transaction.rs b/ironfish-rust-nodejs/src/structs/transaction.rs index 35806ce2c5..14219c6c75 100644 --- a/ironfish-rust-nodejs/src/structs/transaction.rs +++ b/ironfish-rust-nodejs/src/structs/transaction.rs @@ -405,6 +405,12 @@ impl NativeUnsignedTransaction { Ok(Buffer::from(vec)) } + #[napi] + pub fn randomized_public_key(&self) -> String { + let bytes = self.transaction.randomized_public_key_bytes(); + bytes_to_hex(&bytes) + } + #[napi] pub fn public_key_randomness(&self) -> String { let bytes = self.transaction.public_key_randomness().to_bytes(); diff --git a/ironfish-rust/src/keys/mod.rs b/ironfish-rust/src/keys/mod.rs index f6b8ce9b65..9e4313cda6 100644 --- a/ironfish-rust/src/keys/mod.rs +++ b/ironfish-rust/src/keys/mod.rs @@ -24,6 +24,8 @@ mod public_address; pub use public_address::*; mod view_keys; pub use view_keys::*; +mod util; +pub use util::*; pub mod proof_generation_key; pub use proof_generation_key::*; diff --git a/ironfish-rust/src/keys/util.rs b/ironfish-rust/src/keys/util.rs new file mode 100644 index 0000000000..369b518e8a --- /dev/null +++ b/ironfish-rust/src/keys/util.rs @@ -0,0 +1,19 @@ +/* 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/. */ + +use group::GroupEncoding; +use ironfish_zkp::{constants::SPENDING_KEY_GENERATOR, redjubjub}; +use jubjub::Fr; + +use crate::{errors::IronfishError, ViewKey}; + +pub fn generate_randomized_public_key( + view_key: ViewKey, + public_key_randomness: Fr, +) -> Result<[u8; 32], IronfishError> { + let randomized_public_key = redjubjub::PublicKey(view_key.authorizing_key.into()) + .randomize(public_key_randomness, *SPENDING_KEY_GENERATOR); + + Ok(randomized_public_key.0.to_bytes()) +} diff --git a/ironfish-rust/src/transaction/unsigned.rs b/ironfish-rust/src/transaction/unsigned.rs index ac2548d14b..2ff8fd90e2 100644 --- a/ironfish-rust/src/transaction/unsigned.rs +++ b/ironfish-rust/src/transaction/unsigned.rs @@ -321,6 +321,10 @@ impl UnsignedTransaction { self.public_key_randomness } + pub fn randomized_public_key_bytes(&self) -> [u8; 32] { + self.randomized_public_key.0.to_bytes() + } + pub fn outputs(&self) -> &Vec { &self.outputs } diff --git a/ironfish/src/rpc/clients/client.ts b/ironfish/src/rpc/clients/client.ts index 4dcce51d15..0b47bdd5fc 100644 --- a/ironfish/src/rpc/clients/client.ts +++ b/ironfish/src/rpc/clients/client.ts @@ -7,6 +7,8 @@ import type { AcceptTransactionResponse, AddPeerRequest, AddPeerResponse, + AddSignatureRequest, + AddSignatureResponse, AddTransactionRequest, AddTransactionResponse, AggregateSignatureSharesRequest, @@ -161,6 +163,8 @@ import type { SetScanningResponse, ShowChainRequest, ShowChainResponse, + SignTransactionRequest, + SignTransactionResponse, StopNodeResponse, SubmitBlockRequest, SubmitBlockResponse, @@ -172,7 +176,6 @@ import type { UseAccountResponse, } from '../routes' import { ApiNamespace } from '../routes/namespaces' -import { AddSignatureRequest, AddSignatureResponse } from '../routes/wallet/addSignature' export abstract class RpcClient { abstract request( @@ -574,6 +577,15 @@ export abstract class RpcClient { ).waitForEnd() }, + signTransaction: ( + params: SignTransactionRequest, + ): Promise> => { + return this.request( + `${ApiNamespace.wallet}/signTransaction`, + params, + ).waitForEnd() + }, + estimateFeeRates: ( params?: EstimateFeeRatesRequest, ): Promise> => { diff --git a/ironfish/src/rpc/routes/wallet/addSignature.ts b/ironfish/src/rpc/routes/wallet/addSignature.ts index 122712f97a..a21383923b 100644 --- a/ironfish/src/rpc/routes/wallet/addSignature.ts +++ b/ironfish/src/rpc/routes/wallet/addSignature.ts @@ -1,8 +1,9 @@ /* 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 { UnsignedTransaction } from '@ironfish/rust-nodejs' +import { generateRandomizedPublicKey, UnsignedTransaction } from '@ironfish/rust-nodejs' import * as yup from 'yup' +import { Account } from '../../../wallet' import { ApiNamespace } from '../namespaces' import { routes } from '../router' import { AssertHasRpcContext } from '../rpcContext' @@ -14,6 +15,7 @@ export type AddSignatureRequest = { export type AddSignatureResponse = { transaction: string + account: string } export const AddSignatureRequestSchema: yup.ObjectSchema = yup @@ -26,6 +28,7 @@ export const AddSignatureRequestSchema: yup.ObjectSchema = export const AddSignatureResponseSchema: yup.ObjectSchema = yup .object({ transaction: yup.string().defined(), + account: yup.string().defined(), }) .defined() @@ -44,10 +47,24 @@ routes.register( throw new Error('Invalid signature length') } + const publicKeyRandomness = unsignedTransaction.publicKeyRandomness() + const randomizedPublicKey = unsignedTransaction.randomizedPublicKey() + + const account = node.wallet.findAccount( + (account: Account) => + generateRandomizedPublicKey(account.viewKey, publicKeyRandomness) === + randomizedPublicKey, + ) + + if (!account) { + throw new Error('Wallet does not contain sender account for this transaction.') + } + const serialized = unsignedTransaction.addSignature(buffer) request.end({ transaction: serialized.toString('hex'), + account: account.name, }) }, ) diff --git a/ironfish/src/rpc/routes/wallet/index.ts b/ironfish/src/rpc/routes/wallet/index.ts index 742c56b7a2..775f5ebbe5 100644 --- a/ironfish/src/rpc/routes/wallet/index.ts +++ b/ironfish/src/rpc/routes/wallet/index.ts @@ -40,6 +40,7 @@ export * from './resetAccount' export * from './sendTransaction' export * from './setAccountHead' export * from './setScanning' +export * from './signTransaction' export * from './types' export * from './use' export * from './useAccount' diff --git a/ironfish/src/rpc/routes/wallet/signTransaction.ts b/ironfish/src/rpc/routes/wallet/signTransaction.ts new file mode 100644 index 0000000000..24b5a3ce58 --- /dev/null +++ b/ironfish/src/rpc/routes/wallet/signTransaction.ts @@ -0,0 +1,66 @@ +/* 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 { generateRandomizedPublicKey, UnsignedTransaction } from '@ironfish/rust-nodejs' +import * as yup from 'yup' +import { Account } from '../../../wallet' +import { ApiNamespace } from '../namespaces' +import { routes } from '../router' +import { AssertHasRpcContext } from '../rpcContext' + +export type SignTransactionRequest = { + unsignedTransaction: string +} + +export type SignTransactionResponse = { + transaction: string + account: string +} + +export const SignTransactionRequestSchema: yup.ObjectSchema = yup + .object({ + unsignedTransaction: yup.string().defined(), + }) + .defined() + +export const SignTransactionResponseSchema: yup.ObjectSchema = yup + .object({ + transaction: yup.string().defined(), + account: yup.string().defined(), + }) + .defined() + +routes.register( + `${ApiNamespace.wallet}/signTransaction`, + SignTransactionRequestSchema, + (request, context): void => { + AssertHasRpcContext(request, context, 'wallet') + const unsignedTransaction = new UnsignedTransaction( + Buffer.from(request.data.unsignedTransaction, 'hex'), + ) + + const publicKeyRandomness = unsignedTransaction.publicKeyRandomness() + const randomizedPublicKey = unsignedTransaction.randomizedPublicKey() + + const account = context.wallet.findAccount( + (account: Account) => + generateRandomizedPublicKey(account.viewKey, publicKeyRandomness) === + randomizedPublicKey, + ) + + if (!account) { + throw new Error('Wallet does not contain sender account for this transaction.') + } + + if (!account.spendingKey) { + throw new Error('Account does not have a spending key') + } + + const serialized = unsignedTransaction.sign(account.spendingKey) + + request.end({ + transaction: serialized.toString('hex'), + account: account.name, + }) + }, +) From 88f33b19181b49534dc20b7623bceef58d5d2cee Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Tue, 9 Jul 2024 13:15:44 -0700 Subject: [PATCH 33/81] import ledger account through cli (#5100) --- ironfish-cli/src/commands/wallet/import.ts | 41 ++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/ironfish-cli/src/commands/wallet/import.ts b/ironfish-cli/src/commands/wallet/import.ts index 7866558fd4..81cdbb907e 100644 --- a/ironfish-cli/src/commands/wallet/import.ts +++ b/ironfish-cli/src/commands/wallet/import.ts @@ -1,11 +1,17 @@ /* 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 { RPC_ERROR_CODES, RpcRequestError } from '@ironfish/sdk' +import { + AccountFormat, + encodeAccountImport, + RPC_ERROR_CODES, + RpcRequestError, +} from '@ironfish/sdk' import { Args, Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' import { importFile, importPipe, longPrompt } from '../../utils/input' +import { Ledger } from '../../utils/ledger' export class ImportCommand extends IronfishCommand { static description = `Import an account` @@ -26,6 +32,11 @@ export class ImportCommand extends IronfishCommand { createdAt: Flags.integer({ description: 'Block sequence to begin scanning from for the imported account', }), + ledger: Flags.boolean({ + description: 'import a view-only account from a ledger device', + default: false, + exclusive: ['path'], + }), } static args = { @@ -43,14 +54,23 @@ export class ImportCommand extends IronfishCommand { let account: string - if (blob && blob.length !== 0 && flags.path && flags.path.length !== 0) { + if ( + blob && + blob.length !== 0 && + ((flags.path && flags.path.length !== 0) || flags.ledger) + ) { this.error( - `Your command includes an unexpected argument. Please pass either --path or the output of wallet:export.`, + `Your command includes an unexpected argument. Please pass only 1 of the following: + 1. the output of wallet:export OR + 2. --path to import an account from a file OR + 3. --ledger to import an account from a ledger device`, ) } if (blob) { account = blob + } else if (flags.ledger) { + account = await this.importLedger() } else if (flags.path) { account = await importFile(this.sdk.fileSystem, flags.path) } else if (process.stdin.isTTY) { @@ -129,4 +149,19 @@ export class ImportCommand extends IronfishCommand { this.log(`Run "ironfish wallet:use ${name}" to set the account as default`) } } + + async importLedger(): Promise { + try { + const ledger = new Ledger(this.logger) + await ledger.connect() + const account = await ledger.importAccount() + return encodeAccountImport(account, AccountFormat.Base64Json) + } catch (e) { + if (e instanceof Error) { + this.error(e.message) + } else { + this.error('Unknown error while importing account from ledger device.') + } + } + } } From e71b18ff6f0410a215fd0572af69a8193a1289a3 Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Tue, 9 Jul 2024 14:04:48 -0700 Subject: [PATCH 34/81] Add check for locked ledger device (#5120) --- ironfish-cli/src/utils/ledger.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ironfish-cli/src/utils/ledger.ts b/ironfish-cli/src/utils/ledger.ts index 0ebfef5e07..fe472383ec 100644 --- a/ironfish-cli/src/utils/ledger.ts +++ b/ironfish-cli/src/utils/ledger.ts @@ -25,6 +25,10 @@ export class Ledger { connect = async () => { const transport = await TransportNodeHid.create(3000, 3000) + if (transport.deviceModel) { + this.logger.debug(`${transport.deviceModel.productName} found.`) + } + const app = new IronfishApp(transport) const appInfo = await app.appInfo() @@ -32,9 +36,17 @@ export class Ledger { if (appInfo.appName !== 'Ironfish') { this.logger.debug(appInfo.appName ?? 'no app name') - this.logger.debug(appInfo.returnCode.toString()) + this.logger.debug(appInfo.returnCode.toString(16)) this.logger.debug(appInfo.errorMessage.toString()) - throw new Error('Please open the Iron Fish app on your ledger device') + + // references: + // https://github.com/LedgerHQ/ledger-live/blob/173bb3c84cc855f83ab8dc49362bc381afecc31e/libs/ledgerjs/packages/errors/src/index.ts#L263 + // https://github.com/Zondax/ledger-ironfish/blob/bf43a4b8d403d15138699ee3bb1a3d6dfdb428bc/docs/APDUSPEC.md?plain=1#L25 + if (appInfo.returnCode === 0x5515) { + throw new Error('Please unlock your Ledger device.') + } + + throw new Error('Please open the Iron Fish app on your ledger device.') } if (appInfo.appVersion) { From 24dfc3cc4bb417bab8f96fc80e143602160c9662 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Tue, 9 Jul 2024 15:10:53 -0700 Subject: [PATCH 35/81] Rename Wallet.accounts -> Wallet.accountsById (#5123) I think this will allow us to add a new accounts accessor that just returns the accounts itself. --- ironfish/src/wallet/wallet.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index d59a68da29..195bb43696 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.ts @@ -93,7 +93,7 @@ export class Wallet { readonly onAccountImported = new Event<[account: Account]>() readonly onAccountRemoved = new Event<[account: Account]>() - protected readonly accounts = new Map() + protected readonly accountById = new Map() readonly walletDb: WalletDB private readonly logger: Logger readonly workerPool: WorkerPool @@ -210,7 +210,7 @@ export class Wallet { private async load(): Promise { for await (const accountValue of this.walletDb.loadAccounts()) { const account = new Account({ accountValue, walletDb: this.walletDb }) - this.accounts.set(account.id, account) + this.accountById.set(account.id, account) } const meta = await this.walletDb.loadAccountsMeta() @@ -218,7 +218,7 @@ export class Wallet { } private unload(): void { - this.accounts.clear() + this.accountById.clear() this.defaultAccount = null } @@ -1108,7 +1108,7 @@ export class Wallet { } async rebroadcastTransactions(sequence: number): Promise { - for (const account of this.accounts.values()) { + for (const account of this.accountById.values()) { if (this.eventLoopAbortController.signal.aborted) { return } @@ -1152,7 +1152,7 @@ export class Wallet { } async expireTransactions(sequence: number): Promise { - for (const account of this.accounts.values()) { + for (const account of this.accountById.values()) { if (this.eventLoopAbortController.signal.aborted) { return } @@ -1306,7 +1306,7 @@ export class Wallet { await account.updateHead(createdAt, tx) }) - this.accounts.set(account.id, account) + this.accountById.set(account.id, account) if (options.setDefault) { await this.setDefaultAccount(account.name) @@ -1410,7 +1410,7 @@ export class Wallet { } }) - this.accounts.set(account.id, account) + this.accountById.set(account.id, account) this.logger.debug(`Account ${account.id} imported successfully`) this.onAccountImported.emit(account) @@ -1418,7 +1418,7 @@ export class Wallet { } listAccounts(): Account[] { - return Array.from(this.accounts.values()) + return Array.from(this.accountById.values()) } accountExists(name: string): boolean { @@ -1470,7 +1470,7 @@ export class Wallet { this.defaultAccount = newAccount.id } - this.accounts.set(newAccount.id, newAccount) + this.accountById.set(newAccount.id, newAccount) await this.removeAccount(account, tx) }) @@ -1486,7 +1486,7 @@ export class Wallet { } async removeAccount(account: Account, tx?: IDatabaseTransaction): Promise { - this.accounts.delete(account.id) + this.accountById.delete(account.id) await this.walletDb.db.withTransaction(tx, async (tx) => { if (account.id === this.defaultAccount) { @@ -1548,7 +1548,7 @@ export class Wallet { } findAccount(predicate: (account: Account) => boolean): Account | null { - for (const account of this.accounts.values()) { + for (const account of this.accountById.values()) { if (predicate(account)) { return account } @@ -1562,7 +1562,7 @@ export class Wallet { } getAccount(id: string): Account | null { - const account = this.accounts.get(id) + const account = this.accountById.get(id) if (account) { return account @@ -1582,7 +1582,7 @@ export class Wallet { getEarliestHead(tx?: IDatabaseTransaction): Promise { return this.walletDb.db.withTransaction(tx, async (tx) => { let earliestHead = null - for (const account of this.accounts.values()) { + for (const account of this.accountById.values()) { if (!account.scanningEnabled) { continue } @@ -1606,7 +1606,7 @@ export class Wallet { return this.walletDb.db.withTransaction(tx, async (tx) => { let latestHead = null - for (const account of this.accounts.values()) { + for (const account of this.accountById.values()) { if (!account.scanningEnabled) { continue } From 5fcd314a3fbef66744c250bdd49a87d543f11272 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Tue, 9 Jul 2024 15:31:30 -0700 Subject: [PATCH 36/81] Add Wallet.accounts property (#5124) Replace listAccounts() with this new property. --- ironfish-cli/src/commands/repl.ts | 2 +- ironfish-cli/src/commands/wallet/prune.ts | 2 +- ironfish/README.md | 2 +- ironfish/src/rpc/routes/wallet/addTransaction.ts | 2 +- ironfish/src/rpc/routes/wallet/getAccounts.ts | 2 +- ironfish/src/rpc/routes/wallet/getAccountsStatus.ts | 4 +--- ironfish/src/wallet/scanner/walletScanner.ts | 6 ++---- ironfish/src/wallet/wallet.ts | 10 +++++----- 8 files changed, 13 insertions(+), 17 deletions(-) diff --git a/ironfish-cli/src/commands/repl.ts b/ironfish-cli/src/commands/repl.ts index da92431e34..62c06fa8c1 100644 --- a/ironfish-cli/src/commands/repl.ts +++ b/ironfish-cli/src/commands/repl.ts @@ -48,7 +48,7 @@ export default class Repl extends IronfishCommand { this.log('\n Get a block at a sequence') this.log(` > await chain.getHeaderAtSequence(1)`) this.log('\n List all account names') - this.log(` > wallet.listAccounts().map((a) => a.name)`) + this.log(` > wallet.accounts.map((a) => a.name)`) this.log(`\n Get the balance of an account`) this.log(` > const account = await wallet.getAccountByName('default')`) this.log(` > await wallet.getBalances(account)`) diff --git a/ironfish-cli/src/commands/wallet/prune.ts b/ironfish-cli/src/commands/wallet/prune.ts index de9c19ac88..9f81d6b18e 100644 --- a/ironfish-cli/src/commands/wallet/prune.ts +++ b/ironfish-cli/src/commands/wallet/prune.ts @@ -54,7 +54,7 @@ export default class PruneCommand extends IronfishCommand { accounts = [account] } else { - accounts = node.wallet.listAccounts() + accounts = node.wallet.accounts } if (flags.expire) { diff --git a/ironfish/README.md b/ironfish/README.md index b620f3e67b..53026074bf 100644 --- a/ironfish/README.md +++ b/ironfish/README.md @@ -55,5 +55,5 @@ const node = sdk.node() node.start() // List all accounts from the wallet -console.log(await node.wallet.listAccounts()) +console.log(node.wallet.accounts) ``` diff --git a/ironfish/src/rpc/routes/wallet/addTransaction.ts b/ironfish/src/rpc/routes/wallet/addTransaction.ts index 7c9a3301c6..e1083af83f 100644 --- a/ironfish/src/rpc/routes/wallet/addTransaction.ts +++ b/ironfish/src/rpc/routes/wallet/addTransaction.ts @@ -53,7 +53,7 @@ routes.register( await context.wallet.addPendingTransaction(transaction) - const accounts = await AsyncUtils.filter(context.wallet.listAccounts(), (account) => + const accounts = await AsyncUtils.filter(context.wallet.accounts, (account) => account.hasTransaction(transaction.hash()), ) diff --git a/ironfish/src/rpc/routes/wallet/getAccounts.ts b/ironfish/src/rpc/routes/wallet/getAccounts.ts index 234ea3e3ed..608beb46f9 100644 --- a/ironfish/src/rpc/routes/wallet/getAccounts.ts +++ b/ironfish/src/rpc/routes/wallet/getAccounts.ts @@ -38,7 +38,7 @@ routes.register( accounts = [defaultAccount] } } else { - accounts = node.wallet.listAccounts() + accounts = node.wallet.accounts } const names = accounts.map((a) => (request.data?.displayName ? a.displayName : a.name)) diff --git a/ironfish/src/rpc/routes/wallet/getAccountsStatus.ts b/ironfish/src/rpc/routes/wallet/getAccountsStatus.ts index b53bcfcbea..d91a976566 100644 --- a/ironfish/src/rpc/routes/wallet/getAccountsStatus.ts +++ b/ironfish/src/rpc/routes/wallet/getAccountsStatus.ts @@ -32,9 +32,7 @@ routes.register serializeRpcAccountStatus(node.wallet, account)), + node.wallet.accounts.map((account) => serializeRpcAccountStatus(node.wallet, account)), ) request.end({ accounts }) diff --git a/ironfish/src/wallet/scanner/walletScanner.ts b/ironfish/src/wallet/scanner/walletScanner.ts index 860dd4a48e..faeff67bf2 100644 --- a/ironfish/src/wallet/scanner/walletScanner.ts +++ b/ironfish/src/wallet/scanner/walletScanner.ts @@ -267,8 +267,7 @@ export class WalletScanner { */ private haveWalletAccountsChanged(): boolean { const accountIds = new Set( - this.wallet - .listAccounts() + this.wallet.accounts .filter((account) => account.scanningEnabled) .map((account) => account.id), ) @@ -283,8 +282,7 @@ export class WalletScanner { > { return this.wallet.walletDb.db.withTransaction(null, async (tx) => Promise.all( - this.wallet - .listAccounts() + this.wallet.accounts .filter((account) => account.scanningEnabled) .map(async (account) => ({ account, diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index 195bb43696..b1e18f7cb4 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.ts @@ -174,7 +174,7 @@ export class Wallet { return null } - if (this.listAccounts().length === 0) { + if (this.accounts.length === 0) { return null } @@ -340,7 +340,7 @@ export class Wallet { tx?: IDatabaseTransaction, ): Promise { return this.walletDb.db.withTransaction(tx, async (tx) => { - for (const account of this.listAccounts()) { + for (const account of this.accounts) { await this.resetAccount(account, options, tx) } }) @@ -603,7 +603,7 @@ export class Wallet { async addPendingTransaction(transaction: Transaction): Promise { const accounts = await AsyncUtils.filter( - this.listAccounts(), + this.accounts, async (account) => !(await account.hasTransaction(transaction.hash())), ) @@ -1350,7 +1350,7 @@ export class Wallet { } if (accountValue.spendingKey) { - const duplicateSpendingAccount = this.listAccounts().find( + const duplicateSpendingAccount = this.accounts.find( (a) => accountValue.spendingKey === a.spendingKey, ) @@ -1417,7 +1417,7 @@ export class Wallet { return account } - listAccounts(): Account[] { + get accounts(): Account[] { return Array.from(this.accountById.values()) } From 846f90d6d6e58d4839a839ea1efc3334adad1baa Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Wed, 10 Jul 2024 14:40:29 -0700 Subject: [PATCH 37/81] Implement custom CLI table logic (#5118) * Implement custom CLI table logic Since oclif 4.x removes their table feature, lets implement our own. The logic is straight-forward enough that doing this was a reasonable task. The alternative CLI table libraries didn't have the specific features we use, and were generally over-engineered for our use. This does remove some functionality that we were not using, as well as potentially removing some extra usability that users may miss. We can re-add these things as needed. * Implement TableFlags * Update usage of table.Column --- ironfish-cli/package.json | 2 + ironfish-cli/src/commands/chain/genesisadd.ts | 8 +- .../src/commands/chain/genesisblock.ts | 8 +- .../src/commands/mempool/transactions.ts | 8 +- ironfish-cli/src/commands/peers/banned.ts | 8 +- ironfish-cli/src/commands/peers/index.ts | 8 +- ironfish-cli/src/commands/wallet/assets.ts | 9 +- ironfish-cli/src/commands/wallet/balances.ts | 8 +- .../wallet/multisig/participants/index.ts | 4 +- .../src/commands/wallet/notes/combine.ts | 4 +- .../src/commands/wallet/notes/index.ts | 11 +- ironfish-cli/src/commands/wallet/status.ts | 8 +- .../src/commands/wallet/transaction/index.ts | 8 +- .../src/commands/wallet/transactions.ts | 27 +- ironfish-cli/src/ui/confirm.ts | 31 +++ ironfish-cli/src/ui/index.ts | 7 + ironfish-cli/src/{ui.ts => ui/progressBar.ts} | 43 +-- ironfish-cli/src/ui/table.ts | 251 ++++++++++++++++++ ironfish-cli/src/utils/table.ts | 34 +-- yarn.lock | 23 +- 20 files changed, 394 insertions(+), 116 deletions(-) create mode 100644 ironfish-cli/src/ui/confirm.ts create mode 100644 ironfish-cli/src/ui/index.ts rename ironfish-cli/src/{ui.ts => ui/progressBar.ts} (71%) create mode 100644 ironfish-cli/src/ui/table.ts diff --git a/ironfish-cli/package.json b/ironfish-cli/package.json index 69fe699850..9841cd65a3 100644 --- a/ironfish-cli/package.json +++ b/ironfish-cli/package.json @@ -79,6 +79,8 @@ "inquirer": "8.2.5", "json-colorizer": "2.2.2", "keccak": "3.0.4", + "natural-orderby": "3.0.2", + "string-width": "4.2.3", "supports-hyperlinks": "2.2.0", "tar": "6.1.11", "uuid": "8.3.2" diff --git a/ironfish-cli/src/commands/chain/genesisadd.ts b/ironfish-cli/src/commands/chain/genesisadd.ts index 8322822450..947376fba4 100644 --- a/ironfish-cli/src/commands/chain/genesisadd.ts +++ b/ironfish-cli/src/commands/chain/genesisadd.ts @@ -10,11 +10,11 @@ import { IJSON, isValidPublicAddress, } from '@ironfish/sdk' -import { Flags, ux } from '@oclif/core' +import { Flags } from '@oclif/core' import fs from 'fs/promises' import { IronfishCommand } from '../../command' import { LocalFlags } from '../../flags' -import { confirmOrQuit } from '../../ui' +import { confirmOrQuit, table, TableColumns } from '../../ui' export default class GenesisAddCommand extends IronfishCommand { static hidden = true @@ -80,7 +80,7 @@ export default class GenesisAddCommand extends IronfishCommand { // Log genesis block info this.log(`Genesis block will be modified with the following values in a new transaction:`) this.log(`Allocations:`) - const columns: ux.Table.table.Columns = { + const columns: TableColumns = { identity: { header: 'ADDRESS', get: (row: GenesisBlockAllocation) => row.publicAddress, @@ -97,7 +97,7 @@ export default class GenesisAddCommand extends IronfishCommand { }, } - ux.table(allocations, columns, { + table(allocations, columns, { printLine: this.log.bind(this), }) diff --git a/ironfish-cli/src/commands/chain/genesisblock.ts b/ironfish-cli/src/commands/chain/genesisblock.ts index 994277535e..9365e4ba15 100644 --- a/ironfish-cli/src/commands/chain/genesisblock.ts +++ b/ironfish-cli/src/commands/chain/genesisblock.ts @@ -12,11 +12,11 @@ import { makeGenesisBlock, Target, } from '@ironfish/sdk' -import { Flags, ux } from '@oclif/core' +import { Flags } from '@oclif/core' import fs from 'fs/promises' import { IronfishCommand } from '../../command' import { LocalFlags } from '../../flags' -import { confirmOrQuit } from '../../ui' +import { confirmOrQuit, table, TableColumns } from '../../ui' export default class GenesisBlockCommand extends IronfishCommand { static description = 'Create and serialize a genesis block' @@ -135,7 +135,7 @@ export default class GenesisBlockCommand extends IronfishCommand { this.log(`Genesis block will be created with the following values:`) this.log(`\nDifficulty: ${target.toDifficulty()}\n`) this.log(`Allocations:`) - const columns: ux.Table.table.Columns = { + const columns: TableColumns = { identity: { header: 'ADDRESS', get: (row: GenesisBlockAllocation) => row.publicAddress, @@ -152,7 +152,7 @@ export default class GenesisBlockCommand extends IronfishCommand { }, } - ux.table(info.allocations, columns, { + table(info.allocations, columns, { printLine: this.log.bind(this), }) diff --git a/ironfish-cli/src/commands/mempool/transactions.ts b/ironfish-cli/src/commands/mempool/transactions.ts index cc882e79d5..e6c4f12e8d 100644 --- a/ironfish-cli/src/commands/mempool/transactions.ts +++ b/ironfish-cli/src/commands/mempool/transactions.ts @@ -2,11 +2,11 @@ * 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 { getFeeRate, GetMempoolTransactionResponse, MinMax, Transaction } from '@ironfish/sdk' -import { Flags, ux } from '@oclif/core' +import { Flags } from '@oclif/core' import { InferredFlags } from '@oclif/core/lib/interfaces' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' -import { TableFlags } from '../../utils/table' +import { table, TableColumns, TableFlags } from '../../ui' const { sort: _, ...tableFlags } = TableFlags @@ -154,7 +154,7 @@ function renderTable( response: GetMempoolTransactionResponse[], flags: InferredFlags, ): string { - const columns: ux.Table.table.Columns = { + const columns: TableColumns = { position: { header: 'POSITION', minWidth: 4, @@ -210,7 +210,7 @@ function renderTable( let result = '' const limit = flags.csv ? 0 : flags.show - ux.table(getRows(response, limit), columns, { + table(getRows(response, limit), columns, { printLine: (line) => (result += `${String(line)}\n`), ...flags, }) diff --git a/ironfish-cli/src/commands/peers/banned.ts b/ironfish-cli/src/commands/peers/banned.ts index a9678ca643..d8aef88067 100644 --- a/ironfish-cli/src/commands/peers/banned.ts +++ b/ironfish-cli/src/commands/peers/banned.ts @@ -2,11 +2,11 @@ * 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 { BannedPeerResponse, GetBannedPeersResponse, PromiseUtils } from '@ironfish/sdk' -import { Flags, ux } from '@oclif/core' +import { Flags } from '@oclif/core' import blessed from 'blessed' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' -import { TableFlags } from '../../utils/table' +import { table, TableColumns, TableFlags } from '../../ui' const { sort, ...tableFlags } = TableFlags @@ -67,7 +67,7 @@ export class BannedCommand extends IronfishCommand { } function renderTable(content: GetBannedPeersResponse): string { - const columns: ux.Table.table.Columns = { + const columns: TableColumns = { identity: { minWidth: 45, header: 'IDENTITY', @@ -86,7 +86,7 @@ function renderTable(content: GetBannedPeersResponse): string { let result = '' - ux.table(content.peers, columns, { + table(content.peers, columns, { printLine: (line) => (result += `${String(line)}\n`), }) diff --git a/ironfish-cli/src/commands/peers/index.ts b/ironfish-cli/src/commands/peers/index.ts index ab192ddfb2..05bf983f19 100644 --- a/ironfish-cli/src/commands/peers/index.ts +++ b/ironfish-cli/src/commands/peers/index.ts @@ -2,12 +2,12 @@ * 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 { GetPeersResponse, PromiseUtils } from '@ironfish/sdk' -import { Flags, ux } from '@oclif/core' +import { Flags } from '@oclif/core' import { InferredFlags } from '@oclif/core/lib/interfaces' import blessed from 'blessed' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' -import { TableFlags } from '../../utils/table' +import { table, TableColumns, TableFlags } from '../../ui' type GetPeerResponsePeer = GetPeersResponse['peers'][0] @@ -96,7 +96,7 @@ function renderTable( content: GetPeersResponse, flags: InferredFlags, ): string { - let columns: ux.Table.table.Columns = { + let columns: TableColumns = { identity: { header: 'IDENTITY', get: (row: GetPeerResponsePeer) => { @@ -224,7 +224,7 @@ function renderTable( let result = '' - ux.table(peers, columns, { + table(peers, columns, { printLine: (line) => (result += `${String(line)}\n`), ...flags, }) diff --git a/ironfish-cli/src/commands/wallet/assets.ts b/ironfish-cli/src/commands/wallet/assets.ts index b53f691c6d..e7edec57f9 100644 --- a/ironfish-cli/src/commands/wallet/assets.ts +++ b/ironfish-cli/src/commands/wallet/assets.ts @@ -9,11 +9,12 @@ import { PUBLIC_ADDRESS_LENGTH, } from '@ironfish/rust-nodejs' import { BufferUtils } from '@ironfish/sdk' -import { Args, Flags, ux } from '@oclif/core' +import { Args, Flags } from '@oclif/core' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' +import { table, TableFlags } from '../../ui' import { renderAssetWithVerificationStatus } from '../../utils' -import { TableCols, TableFlags } from '../../utils/table' +import { TableCols } from '../../utils/table' const MAX_ASSET_METADATA_COLUMN_WIDTH = ASSET_METADATA_LENGTH + 1 const MIN_ASSET_METADATA_COLUMN_WIDTH = ASSET_METADATA_LENGTH / 2 + 1 @@ -59,7 +60,7 @@ export class AssetsCommand extends IronfishCommand { let showHeader = !flags['no-header'] for await (const asset of response.contentStream()) { - ux.table( + table( [asset], { name: TableCols.fixedWidth({ @@ -77,6 +78,7 @@ export class AssetsCommand extends IronfishCommand { id: { header: 'ID', minWidth: ASSET_ID_LENGTH + 1, + get: (row) => row.id, }, metadata: TableCols.fixedWidth({ header: 'Metadata', @@ -85,6 +87,7 @@ export class AssetsCommand extends IronfishCommand { }), createdTransactionHash: { header: 'Created Transaction Hash', + get: (row) => row.createdTransactionHash, }, supply: { header: 'Supply', diff --git a/ironfish-cli/src/commands/wallet/balances.ts b/ironfish-cli/src/commands/wallet/balances.ts index a8f59762c6..5a3386e7e6 100644 --- a/ironfish-cli/src/commands/wallet/balances.ts +++ b/ironfish-cli/src/commands/wallet/balances.ts @@ -2,11 +2,11 @@ * 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 { BufferUtils, CurrencyUtils, GetBalancesResponse, RpcAsset } from '@ironfish/sdk' -import { Args, Flags, ux } from '@oclif/core' +import { Args, Flags } from '@oclif/core' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' +import { table, TableColumns, TableFlags } from '../../ui' import { compareAssets, renderAssetWithVerificationStatus } from '../../utils' -import { TableFlags } from '../../utils/table' type AssetBalancePairs = { asset: RpcAsset; balance: GetBalancesResponse['balances'][number] } @@ -64,7 +64,7 @@ export class BalancesCommand extends IronfishCommand { }) } - let columns: ux.Table.table.Columns = { + let columns: TableColumns = { assetName: { header: 'Asset Name', get: ({ asset }) => @@ -129,6 +129,6 @@ export class BalancesCommand extends IronfishCommand { ), ) - ux.table(assetBalancePairs, columns, { ...flags }) + table(assetBalancePairs, columns, { ...flags }) } } diff --git a/ironfish-cli/src/commands/wallet/multisig/participants/index.ts b/ironfish-cli/src/commands/wallet/multisig/participants/index.ts index 7c8e7c5bf8..a765401409 100644 --- a/ironfish-cli/src/commands/wallet/multisig/participants/index.ts +++ b/ironfish-cli/src/commands/wallet/multisig/participants/index.ts @@ -1,9 +1,9 @@ /* 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 { ux } from '@oclif/core' import { IronfishCommand } from '../../../../command' import { RemoteFlags } from '../../../../flags' +import { table } from '../../../../ui' export class MultisigParticipants extends IronfishCommand { static description = 'List out all the participant names and identities' @@ -27,7 +27,7 @@ export class MultisigParticipants extends IronfishCommand { // sort identities by name participants.sort((a, b) => a.name.localeCompare(b.name)) - ux.table( + table( participants, { name: { diff --git a/ironfish-cli/src/commands/wallet/notes/combine.ts b/ironfish-cli/src/commands/wallet/notes/combine.ts index 4649d15256..fbc0a75068 100644 --- a/ironfish-cli/src/commands/wallet/notes/combine.ts +++ b/ironfish-cli/src/commands/wallet/notes/combine.ts @@ -16,7 +16,7 @@ import { Flags, ux } from '@oclif/core' import inquirer from 'inquirer' import { IronfishCommand } from '../../../command' import { HexFlag, IronFlag, RemoteFlags } from '../../../flags' -import { confirmOrQuit } from '../../../ui' +import { confirmOrQuit, table } from '../../../ui' import { getAssetsByIDs, selectAsset } from '../../../utils' import { getExplorer } from '../../../utils/explorer' import { selectFee } from '../../../utils/fees' @@ -452,7 +452,7 @@ export class CombineNotesCommand extends IronfishCommand { if (resultingNotes) { this.log('') - ux.table( + table( resultingNotes, { hash: { diff --git a/ironfish-cli/src/commands/wallet/notes/index.ts b/ironfish-cli/src/commands/wallet/notes/index.ts index e0da85a6a4..ec99ddee2b 100644 --- a/ironfish-cli/src/commands/wallet/notes/index.ts +++ b/ironfish-cli/src/commands/wallet/notes/index.ts @@ -2,10 +2,11 @@ * 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 { CurrencyUtils, RpcAsset } from '@ironfish/sdk' -import { Args, Flags, ux } from '@oclif/core' +import { Args, Flags } from '@oclif/core' import { IronfishCommand } from '../../../command' import { RemoteFlags } from '../../../flags' -import { TableCols, TableFlags } from '../../../utils/table' +import { table, TableFlags } from '../../../ui' +import { TableCols } from '../../../utils/table' const { sort: _, ...tableFlags } = TableFlags export class NotesCommand extends IronfishCommand { @@ -48,19 +49,22 @@ export class NotesCommand extends IronfishCommand { ) } - ux.table( + table( [note], { memo: { header: 'Memo', // Maximum memo length is 32 bytes minWidth: 33, + get: (row) => row.memo, }, sender: { header: 'Sender', + get: (row) => row.sender, }, transactionHash: { header: 'From Transaction', + get: (row) => row.transactionHash, }, isSpent: { header: 'Spent', @@ -86,6 +90,7 @@ export class NotesCommand extends IronfishCommand { }, noteHash: { header: 'Note Hash', + get: (row) => row.noteHash, }, nullifier: { header: 'Nullifier', diff --git a/ironfish-cli/src/commands/wallet/status.ts b/ironfish-cli/src/commands/wallet/status.ts index 9aa2a49b6d..30b3cb8c96 100644 --- a/ironfish-cli/src/commands/wallet/status.ts +++ b/ironfish-cli/src/commands/wallet/status.ts @@ -1,11 +1,10 @@ /* 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 { ux } from '@oclif/core' import chalk from 'chalk' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' -import { TableFlags } from '../../utils/table' +import { table, TableFlags } from '../../ui' export class StatusCommand extends IronfishCommand { static description = `Get status of all accounts` @@ -22,17 +21,20 @@ export class StatusCommand extends IronfishCommand { const response = await client.wallet.getAccountsStatus() - ux.table( + table( response.content.accounts, { name: { + get: (row) => row.name, header: 'Account Name', minWidth: 11, }, id: { + get: (row) => row.id, header: 'Account ID', }, viewOnly: { + get: (row) => row.viewOnly, header: 'View Only', }, headHash: { diff --git a/ironfish-cli/src/commands/wallet/transaction/index.ts b/ironfish-cli/src/commands/wallet/transaction/index.ts index 9804bb67cc..b6b0b821e8 100644 --- a/ironfish-cli/src/commands/wallet/transaction/index.ts +++ b/ironfish-cli/src/commands/wallet/transaction/index.ts @@ -12,6 +12,7 @@ import { import { Args, Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../../command' import { RemoteFlags } from '../../../flags' +import { table } from '../../../ui' import { displayChainportTransactionSummary, extractChainportDataFromTransaction, @@ -126,7 +127,7 @@ export class TransactionCommand extends IronfishCommand { }) } - ux.table(noteAssetPairs, { + table(noteAssetPairs, { amount: { header: 'Amount', get: ({ asset, note }) => @@ -157,7 +158,7 @@ export class TransactionCommand extends IronfishCommand { if (transaction.spends.length > 0) { this.log(`\n---Spends---\n`) - ux.table(transaction.spends, { + table(transaction.spends, { size: { header: 'Size', get: (spend) => spend.size, @@ -183,9 +184,10 @@ export class TransactionCommand extends IronfishCommand { ) this.log(`\n---Asset Balance Deltas---\n`) - ux.table(assetBalanceDeltas, { + table(assetBalanceDeltas, { assetId: { header: 'Asset ID', + get: (assetBalanceDelta) => assetBalanceDelta.assetId, }, delta: { header: 'Balance Change', diff --git a/ironfish-cli/src/commands/wallet/transactions.ts b/ironfish-cli/src/commands/wallet/transactions.ts index a1513e1ce9..0c09e2e6a3 100644 --- a/ironfish-cli/src/commands/wallet/transactions.ts +++ b/ironfish-cli/src/commands/wallet/transactions.ts @@ -10,12 +10,13 @@ import { RpcAsset, TransactionType, } from '@ironfish/sdk' -import { Args, Flags, ux } from '@oclif/core' +import { Args, Flags } from '@oclif/core' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' +import { table, TableColumns, TableFlags } from '../../ui' import { getAssetsByIDs } from '../../utils' import { extractChainportDataFromTransaction } from '../../utils/chainport' -import { Format, TableCols, TableFlags } from '../../utils/table' +import { Format, TableCols } from '../../utils/table' const { sort: _, ...tableFlags } = TableFlags export class TransactionsCommand extends IronfishCommand { @@ -68,8 +69,6 @@ export class TransactionsCommand extends IronfishCommand { ? Format.csv : flags.output === 'json' ? Format.json - : flags.output === 'yaml' - ? Format.yaml : Format.cli const client = await this.sdk.connectRpc() @@ -120,7 +119,7 @@ export class TransactionsCommand extends IronfishCommand { transactionRows = this.getTransactionRows(assetLookup, transaction, format) } - ux.table(transactionRows, columns, { + table(transactionRows, columns, { printLine: this.log.bind(this), ...flags, 'no-header': !showHeader, @@ -260,48 +259,57 @@ export class TransactionsCommand extends IronfishCommand { extended: boolean, notes: boolean, format: Format, - ): ux.Table.table.Columns> { - let columns: ux.Table.table.Columns> = { + ): TableColumns> { + let columns: TableColumns> = { timestamp: TableCols.timestamp({ streaming: true, }), status: { header: 'Status', minWidth: 12, + get: (row) => row.status ?? '', }, type: { header: 'Type', minWidth: notes ? 18 : 8, + get: (row) => row.type ?? '', }, hash: { header: 'Hash', minWidth: 32, + get: (row) => row.hash ?? '', }, notesCount: { header: 'Notes', minWidth: 5, extended: true, + get: (row) => row.notesCount ?? '', }, spendsCount: { header: 'Spends', minWidth: 5, extended: true, + get: (row) => row.spendsCount ?? '', }, mintsCount: { header: 'Mints', minWidth: 5, extended: true, + get: (row) => row.mintsCount ?? '', }, burnsCount: { header: 'Burns', minWidth: 5, extended: true, + get: (row) => row.burnsCount ?? '', }, expiration: { header: 'Expiration', + get: (row) => row.expiration ?? '', }, submittedSequence: { header: 'Submitted Sequence', + get: (row) => row.submittedSequence ?? '', }, feePaid: { header: 'Fee Paid ($IRON)', @@ -327,12 +335,15 @@ export class TransactionsCommand extends IronfishCommand { ...columns, sender: { header: 'Sender Address', + get: (row) => row.sender ?? '', }, recipient: { header: 'Recipient Address', + get: (row) => row.recipient ?? '', }, memo: { header: 'Memo', + get: (row) => row.memo ?? '', }, } } @@ -342,6 +353,7 @@ export class TransactionsCommand extends IronfishCommand { group: { header: '', minWidth: 3, + get: (row) => row.group ?? '', }, ...columns, } @@ -385,4 +397,5 @@ type TransactionRow = { submittedSequence: number sender: string recipient: string + memo?: string } diff --git a/ironfish-cli/src/ui/confirm.ts b/ironfish-cli/src/ui/confirm.ts new file mode 100644 index 0000000000..121f60d01f --- /dev/null +++ b/ironfish-cli/src/ui/confirm.ts @@ -0,0 +1,31 @@ +/* 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 { ux } from '@oclif/core' +import inquirer from 'inquirer' + +export async function confirmPrompt(message: string): Promise { + const result: { prompt: boolean } = await inquirer.prompt({ + type: 'confirm', + // Add a new-line for readability, manually. If the prefix is set to a new-line, it seems to + // add a space before the message, which is unwanted. + message: `\n${message}`, + name: 'prompt', + prefix: '', + }) + return result.prompt +} + +export async function confirmOrQuit(message?: string, confirm?: boolean): Promise { + if (confirm) { + return + } + + const confirmed = await confirmPrompt(message || 'Do you confirm?') + + if (!confirmed) { + ux.log('Operation aborted.') + ux.exit(0) + } +} diff --git a/ironfish-cli/src/ui/index.ts b/ironfish-cli/src/ui/index.ts new file mode 100644 index 0000000000..af9f47d608 --- /dev/null +++ b/ironfish-cli/src/ui/index.ts @@ -0,0 +1,7 @@ +/* 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/. */ + +export * from './confirm' +export * from './progressBar' +export * from './table' diff --git a/ironfish-cli/src/ui.ts b/ironfish-cli/src/ui/progressBar.ts similarity index 71% rename from ironfish-cli/src/ui.ts rename to ironfish-cli/src/ui/progressBar.ts index cfb5ee226e..b7c0b15e2b 100644 --- a/ironfish-cli/src/ui.ts +++ b/ironfish-cli/src/ui/progressBar.ts @@ -3,28 +3,26 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { Assert, Meter, TimeUtils } from '@ironfish/sdk' -import { ux } from '@oclif/core' import * as cliProgress from 'cli-progress' -import inquirer from 'inquirer' -const progressBarCompleteChar = '\u2588' -const progressBarIncompleteChar = '\u2591' +const PROGRESS_BAR_COMPLETE_CHAR = '\u2588' +const PROGRESS_BAR_INCOMPLETE_CHAR = '\u2591' export const ProgressBarPresets = { basic: { - barCompleteChar: progressBarCompleteChar, - barIncompleteChar: progressBarIncompleteChar, + barCompleteChar: PROGRESS_BAR_COMPLETE_CHAR, + barIncompleteChar: PROGRESS_BAR_INCOMPLETE_CHAR, format: '{title}: [{bar}] {percentage}% | ETA: {estimate}', }, default: { - barCompleteChar: progressBarCompleteChar, - barIncompleteChar: progressBarIncompleteChar, + barCompleteChar: PROGRESS_BAR_COMPLETE_CHAR, + barIncompleteChar: PROGRESS_BAR_INCOMPLETE_CHAR, format: '{title}: [{bar}] {percentage}% | {formattedValue} / {formattedTotal} | ETA: {estimate}', }, withSpeed: { - barCompleteChar: progressBarCompleteChar, - barIncompleteChar: progressBarIncompleteChar, + barCompleteChar: PROGRESS_BAR_COMPLETE_CHAR, + barIncompleteChar: PROGRESS_BAR_INCOMPLETE_CHAR, format: '{title}: [{bar}] {percentage}% | {formattedValue} / {formattedTotal} | {speed} / sec | ETA: {estimate}', }, @@ -111,28 +109,3 @@ export class ProgressBar { this.bar.setTotal(total) } } - -export async function confirmPrompt(message: string): Promise { - const result: { prompt: boolean } = await inquirer.prompt({ - type: 'confirm', - // Add a new-line for readability, manually. If the prefix is set to a new-line, it seems to - // add a space before the message, which is unwanted. - message: `\n${message}`, - name: 'prompt', - prefix: '', - }) - return result.prompt -} - -export async function confirmOrQuit(message?: string, confirm?: boolean): Promise { - if (confirm) { - return - } - - const confirmed = await confirmPrompt(message || 'Do you confirm?') - - if (!confirmed) { - ux.log('Operation aborted.') - ux.exit(0) - } -} diff --git a/ironfish-cli/src/ui/table.ts b/ironfish-cli/src/ui/table.ts new file mode 100644 index 0000000000..5d18116e3e --- /dev/null +++ b/ironfish-cli/src/ui/table.ts @@ -0,0 +1,251 @@ +/* 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 } from '@ironfish/sdk' +import { Flags, ux } from '@oclif/core' +import chalk from 'chalk' +import { orderBy } from 'natural-orderby' +import stringWidth from 'string-width' + +const WIDE_DASH = '─' + +export interface TableColumn> { + // The return type of this function can be extended, it's really just to avoid + // being `unknown`. Anything that has a `.toString()` function can be added + // here with no extra changes. + get(this: void, row: T): string | number | bigint | boolean + header: string + extended?: boolean + minWidth?: number +} + +export type TableColumns> = { [key: string]: TableColumn } + +export interface TableOptions { + [key: string]: unknown + extended?: boolean + 'no-header'?: boolean + output?: string + printLine?(this: void, s: unknown): unknown + sort?: string +} + +export const TableFlags = { + csv: Flags.boolean({ + description: 'output is csv format [alias: --output=csv]', + }), + extended: Flags.boolean({ + description: 'show extra columns', + }), + 'no-header': Flags.boolean({ + description: 'hide table header from output', + exclusive: ['csv'], + }), + output: Flags.string({ + description: 'output in a more machine friendly format', + exclusive: ['csv'], + options: ['csv', 'json'], + }), + sort: Flags.string({ + description: "property to sort by (prepend '-' for descending)", + }), +} + +export function table>( + data: T[], + columns: TableColumns, + options: TableOptions = {}, +): void { + new Table(data, columns, options).render() +} + +class Table> { + data: T[] + columns: (TableColumn & { extended: boolean; key: string; width?: number })[] + options: TableOptions & { extended: boolean; printLine(s: unknown): unknown } + + constructor(data: T[], columns: TableColumns, options: TableOptions) { + this.data = data + this.columns = Object.entries(columns).map(([key, column]) => { + const extended = column.extended || false + return { + ...column, + extended, + key, + } + }) + this.options = { + extended: options.extended || false, + 'no-header': options['no-header'], + output: options.csv ? 'csv' : options.output, + printLine: options.printLine ?? ux.log.bind(ux), + sort: options.sort, + } + } + + render() { + // Generate the rendered text to be displayed + let rows: Record[] = [] + for (const data of this.data) { + const row: Record = {} + for (const column of this.columns) { + // Only show columns marked as extended if the table is set to show + // extended columns + if (!this.options.extended && column.extended === true) { + continue + } + row[column.key] = column.get(data).toString() + } + rows.push(row) + } + + // Sort the rows given the column to sort by + if (this.options.sort) { + let sortOrder: 'asc' | 'desc' + let header: string + if (this.options.sort[0] === '-') { + sortOrder = 'desc' + header = this.options.sort.slice(1) + } else { + sortOrder = 'asc' + header = this.options.sort + } + + const column = this.columns.find((c) => c.header.toLowerCase() === header.toLowerCase()) + Assert.isNotUndefined(column, `No column found with name '${header}'`) + + rows = orderBy(rows, column.key, sortOrder) + } + + switch (this.options.output) { + case 'csv': { + this.renderCsv(rows) + break + } + + case 'json': { + this.renderJson(rows) + break + } + + default: { + this.renderTerminal(rows) + } + } + } + + renderCsv(rows: Record[]) { + const columnHeaders = [] + for (const column of this.columns) { + // Only show columns marked as extended if the table is set to show + // extended columns + if (!this.options.extended && column.extended === true) { + continue + } + columnHeaders.push(sanitizeCsvValue(column.header)) + } + this.options.printLine(columnHeaders.join(',')) + + for (const row of rows) { + const rowValues = [] + for (const value of Object.values(row)) { + rowValues.push(sanitizeCsvValue(value)) + } + this.options.printLine(rowValues.join(',')) + } + } + + renderJson(rows: Record[]) { + this.options.printLine(JSON.stringify(rows, null, 2)) + } + + renderTerminal(rows: Record[]) { + // Find column lengths + for (const column of this.columns) { + column.width = maxColumnLength(column, rows) + } + + if (!this.options['no-header']) { + // Print headers + const columnHeaders = [] + for (const column of this.columns) { + // Only show columns marked as extended if the table is set to show + // extended columns + if (!this.options.extended && column.extended === true) { + continue + } + Assert.isNotUndefined(column.width) + const spacerLength = column.width - stringWidth(column.header) + columnHeaders.push(`${column.header}${' '.repeat(spacerLength)}`) + } + this.options.printLine(chalk.bold(` ${columnHeaders.join(' ')}`)) + + // Print header underline + const columnUnderlines = [] + for (const column of this.columns) { + // Only show columns marked as extended if the table is set to show + // extended columns + if (!this.options.extended && column.extended === true) { + continue + } + Assert.isNotUndefined(column.width) + columnUnderlines.push(WIDE_DASH.repeat(column.width)) + } + this.options.printLine(chalk.bold(` ${columnUnderlines.join(' ')}`)) + } + + // Print rows + for (const row of rows) { + const rowValues = [] + for (const [key, value] of Object.entries(row)) { + const column = this.columns.find((c) => c.key === key) + Assert.isNotUndefined(column) + Assert.isNotUndefined(column.width) + const spacerLength = column.width - stringWidth(value) + rowValues.push(`${value}${' '.repeat(spacerLength)}`) + } + this.options.printLine(` ${rowValues.join(' ')}`) + } + } +} + +function maxColumnLength>( + column: { key: string; header: string; minWidth?: number }, + data: T[], +): number { + let maxLength = stringWidth(column.header) + + for (const row of data) { + const length = stringWidth(row[column.key]) + if (length > maxLength) { + maxLength = length + } + } + + if (column.minWidth != null && column.minWidth > maxLength) { + return column.minWidth + } + + return maxLength +} + +function sanitizeCsvValue(value: string): string { + const newValue = value + + // Double-quotes must be escaped with another double-quote + newValue.replace('"', '""') + + // If the value contains any of these special characters, it needs to be + // wrapped in double-quotes + if ( + newValue.includes('"') || + newValue.includes('\r') || + newValue.includes('\n') || + newValue.includes(',') + ) { + return `"${newValue}"` + } + + return newValue +} diff --git a/ironfish-cli/src/utils/table.ts b/ironfish-cli/src/utils/table.ts index a569bd5e94..60220eb957 100644 --- a/ironfish-cli/src/utils/table.ts +++ b/ironfish-cli/src/utils/table.ts @@ -4,8 +4,7 @@ import { ASSET_NAME_LENGTH } from '@ironfish/rust-nodejs' import { Assert, BufferUtils, TimeUtils } from '@ironfish/sdk' -import { Flags, ux } from '@oclif/core' -import { table } from '@oclif/core/lib/cli-ux/styled/table' +import { TableColumn } from '../ui' /** * Estimated max length of the longest TimeUtils.renderTime() @@ -28,7 +27,7 @@ const timestamp = >(options?: { field?: string get?: (row: Record) => string minWidth?: number -}): Partial> => { +}): TableColumn => { const header = options?.header ?? 'Timestamp' const field = options?.field ?? 'timestamp' @@ -63,12 +62,15 @@ const timestamp = >(options?: { const asset = >(options?: { extended?: boolean format?: Format -}): Partial>> => { +}): Partial>> => { if (options?.extended || options?.format !== Format.cli) { return { assetId: { header: 'Asset ID', - get: (row) => row['assetId'], + get: (row) => { + Assert.isString(row.assetId) + return row.assetId + }, minWidth: MAX_ASSET_NAME_COLUMN_WIDTH, extended: options?.extended ?? false, }, @@ -107,9 +109,9 @@ const asset = >(options?: { const fixedWidth = >(options: { width: number get: (row: T) => string - header?: string + header: string extended?: boolean -}): Partial> => { +}): TableColumn => { return { ...options, get: (row) => truncateCol(options.get(row), options.width), @@ -129,24 +131,6 @@ export enum Format { cli = 'cli', csv = 'csv', json = 'json', - yaml = 'yaml', } export const TableCols = { timestamp, asset, fixedWidth } - -const { 'no-truncate': _, ...tableFlags } = ux.table.flags() - -export const TableFlags = { - ...tableFlags, - truncate: Flags.boolean({ - description: 'truncate output to fit screen', - default: false, - allowNo: true, - }), - 'no-truncate': Flags.boolean({ - hidden: true, - default(context) { - return Promise.resolve(!context.flags['truncate']) - }, - }), -} diff --git a/yarn.lock b/yarn.lock index 8092325436..38709b4636 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7975,6 +7975,11 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= +natural-orderby@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/natural-orderby/-/natural-orderby-3.0.2.tgz#1b874d685fbd68beab2c6e7d14f298e03d631ec3" + integrity sha512-x7ZdOwBxZCEm9MM7+eQCjkrNLrW3rkBKNHVr78zbtqnMGVNlnDi6C/eUEYgxHNrcbu0ymvjzcwIL/6H1iHri9g== + natural-orderby@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/natural-orderby/-/natural-orderby-2.0.3.tgz#8623bc518ba162f8ff1cdb8941d74deb0fdcc016" @@ -9827,6 +9832,15 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-width@4.2.3, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -9836,15 +9850,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" From c02055beaec9f2271378b02f665c1d35177328ff Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Thu, 11 Jul 2024 07:49:14 -0700 Subject: [PATCH 38/81] add ledger flag to send command (#5122) --- ironfish-cli/src/commands/wallet/send.ts | 90 ++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/ironfish-cli/src/commands/wallet/send.ts b/ironfish-cli/src/commands/wallet/send.ts index 54507f4975..96c88d8f50 100644 --- a/ironfish-cli/src/commands/wallet/send.ts +++ b/ironfish-cli/src/commands/wallet/send.ts @@ -8,6 +8,7 @@ import { isValidPublicAddress, RawTransaction, RawTransactionSerde, + RpcClient, TimeUtils, Transaction, } from '@ironfish/sdk' @@ -20,6 +21,7 @@ import { promptCurrency } from '../../utils/currency' import { promptExpiration } from '../../utils/expiration' import { getExplorer } from '../../utils/explorer' import { selectFee } from '../../utils/fees' +import { Ledger } from '../../utils/ledger' import { getSpendPostTimeInMs, updateSpendPostTimeInMs } from '../../utils/spendPostTime' import { displayTransactionSummary, @@ -110,6 +112,10 @@ export class Send extends IronfishCommand { description: 'The note hashes to include in the transaction', multiple: true, }), + ledger: Flags.boolean({ + default: false, + description: 'Send a transaction using a Ledger device', + }), } async start(): Promise { @@ -273,6 +279,11 @@ export class Send extends IronfishCommand { this.exit(0) } + if (flags.ledger) { + await this.sendTransactionWithLedger(client, raw, from, flags.watch, flags.confirm) + this.exit(0) + } + const spendPostTime = getSpendPostTimeInMs(this.sdk) const transactionTimer = new TransactionTimer(spendPostTime, raw) @@ -357,4 +368,83 @@ export class Send extends IronfishCommand { }) } } + + private async sendTransactionWithLedger( + client: RpcClient, + raw: RawTransaction, + from: string | undefined, + watch: boolean, + confirm: boolean, + ): Promise { + const ledger = new Ledger(this.logger) + try { + await ledger.connect() + } catch (e) { + if (e instanceof Error) { + this.error(e.message) + } else { + throw e + } + } + + const publicKey = (await client.wallet.getAccountPublicKey({ account: from })).content + .publicKey + + const ledgerPublicKey = await ledger.getPublicAddress() + + if (publicKey !== ledgerPublicKey) { + this.error( + `The public key on the ledger device does not match the public key of the account '${from}'`, + ) + } + + const buildTransactionResponse = await client.wallet.buildTransaction({ + account: from, + rawTransaction: RawTransactionSerde.serialize(raw).toString('hex'), + }) + + const unsignedTransaction = buildTransactionResponse.content.unsignedTransaction + + const signature = (await ledger.sign(unsignedTransaction)).toString('hex') + + this.log(`\nSignature: ${signature}`) + + const addSignatureResponse = await client.wallet.addSignature({ + unsignedTransaction, + signature, + }) + + const signedTransaction = addSignatureResponse.content.transaction + const bytes = Buffer.from(signedTransaction, 'hex') + + const transaction = new Transaction(bytes) + + this.log(`\nSigned Transaction: ${signedTransaction}`) + this.log(`\nHash: ${transaction.hash().toString('hex')}`) + this.log(`Fee: ${CurrencyUtils.render(transaction.fee(), true)}`) + + await confirmOrQuit('', confirm) + + const addTransactionResponse = await client.wallet.addTransaction({ + transaction: signedTransaction, + broadcast: true, + }) + + if (addTransactionResponse.content.accepted === false) { + this.error( + `Transaction '${transaction.hash().toString('hex')}' was not accepted into the mempool`, + ) + } + + if (watch) { + this.log('') + + await watchTransaction({ + client, + logger: this.logger, + account: from, + hash: transaction.hash().toString('hex'), + }) + } + } } From 2e42f19049f4d84ddffe23d07085eee1c19bfdf7 Mon Sep 17 00:00:00 2001 From: andrea Date: Mon, 8 Jul 2024 17:22:40 -0700 Subject: [PATCH 39/81] Use an optimized method for note decryption when multiple accounts are present When multiple accounts are present, instead of decrypting each note for each account, one by one, use an optimized method that can save some computation cycles. --- Cargo.lock | 2 +- Cargo.toml | 2 +- ironfish-rust-nodejs/Cargo.toml | 2 +- ironfish-rust-nodejs/index.d.ts | 1 + ironfish-rust-nodejs/src/stats.rs | 3 + .../src/structs/note_encrypted.rs | 73 +++++++++++++------ ironfish-rust/Cargo.toml | 2 +- ironfish-rust/src/keys/view_keys.rs | 44 ++++++++++- ironfish-rust/src/merkle_note.rs | 42 +++++++++-- ironfish-rust/src/note.rs | 17 +++-- ironfish/src/primitives/noteEncrypted.ts | 12 +++ ironfish/src/workerPool/tasks/decryptNotes.ts | 9 ++- supply-chain/audits.toml | 2 +- supply-chain/config.toml | 2 +- 14 files changed, 167 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1411f1663..daf2fdc4a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1587,7 +1587,7 @@ dependencies = [ [[package]] name = "jubjub" version = "0.9.0" -source = "git+https://github.com/iron-fish/jubjub.git?branch=blstrs#6fac532975e23acd3fa670bc0383d027f09403e5" +source = "git+https://github.com/iron-fish/jubjub.git?branch=blstrs#531157cfa7b81ade207e819ef50c563843b10e30" dependencies = [ "bitvec", "blst", diff --git a/Cargo.toml b/Cargo.toml index 7ceea9ac2a..955ccc7d88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,4 @@ homepage = "https://ironfish.network/" repository = "https://github.com/iron-fish/ironfish" [profile.release] -debug = true \ No newline at end of file +debug = true diff --git a/ironfish-rust-nodejs/Cargo.toml b/ironfish-rust-nodejs/Cargo.toml index d1c153def1..0850f2b6c0 100644 --- a/ironfish-rust-nodejs/Cargo.toml +++ b/ironfish-rust-nodejs/Cargo.toml @@ -33,7 +33,7 @@ ironfish = { path = "../ironfish-rust" } ironfish-frost = { git = "https://github.com/iron-fish/ironfish-frost.git", branch = "main" } napi = { version = "2.13.2", features = ["napi6"] } napi-derive = "2.13.0" -jubjub = { git = "https://github.com/iron-fish/jubjub.git", branch = "blstrs" } +jubjub = { git = "https://github.com/iron-fish/jubjub.git", branch = "blstrs", features = ["multiply-many"] } rand = "0.8.5" num_cpus = "1.16.0" signal-hook = { version = "0.3.17", optional = true, default-features = false, features = ["iterator"] } diff --git a/ironfish-rust-nodejs/index.d.ts b/ironfish-rust-nodejs/index.d.ts index 7af18418a0..c7879d3b74 100644 --- a/ironfish-rust-nodejs/index.d.ts +++ b/ironfish-rust-nodejs/index.d.ts @@ -133,6 +133,7 @@ export class NoteEncrypted { static combineHash(depth: number, jsLeft: Buffer, jsRight: Buffer): Buffer /** Returns undefined if the note was unable to be decrypted with the given key. */ decryptNoteForOwner(incomingHexKey: string): Buffer | null + decryptNoteForOwners(incomingHexKeys: Array): Array /** Returns undefined if the note was unable to be decrypted with the given key. */ decryptNoteForSpender(outgoingHexKey: string): Buffer | null } diff --git a/ironfish-rust-nodejs/src/stats.rs b/ironfish-rust-nodejs/src/stats.rs index 82a4b03719..3b52f5b567 100644 --- a/ironfish-rust-nodejs/src/stats.rs +++ b/ironfish-rust-nodejs/src/stats.rs @@ -29,6 +29,7 @@ fn print_stats(colors: bool) { {highlight}Elliptic Curve Point Multiplication Stats:\n\ • affine muls: {affine_muls}\n\ • extended muls: {extended_muls}\n\ + • extended vector muls: {extended_mul_many_calls} calls / {extended_mul_many_operands} points\n\ Note Encryption Stats:\n\ • total: {note_construct}\n\ Note Decryption Stats:\n\ @@ -39,6 +40,8 @@ fn print_stats(colors: bool) { reset = if colors { "\x1b[0m" } else { "" }, affine_muls = ecpm_stats.affine_muls, extended_muls = ecpm_stats.extended_muls, + extended_mul_many_calls = ecpm_stats.extended_mul_many_calls, + extended_mul_many_operands = ecpm_stats.extended_mul_many_operands, note_construct = note_stats.construct, note_dec_for_owner = note_stats.decrypt_note_for_owner.total, note_dec_for_owner_ok = note_stats.decrypt_note_for_owner.successful, diff --git a/ironfish-rust-nodejs/src/structs/note_encrypted.rs b/ironfish-rust-nodejs/src/structs/note_encrypted.rs index 190a8ab326..07a8d9363e 100644 --- a/ironfish-rust-nodejs/src/structs/note_encrypted.rs +++ b/ironfish-rust-nodejs/src/structs/note_encrypted.rs @@ -2,20 +2,20 @@ * 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/. */ +use crate::to_napi_err; +use ironfish::merkle_note::NOTE_ENCRYPTION_KEY_SIZE; +use ironfish::note::ENCRYPTED_NOTE_SIZE; +use ironfish::note::PLAINTEXT_NOTE_SIZE; +use ironfish::serializing::aead::MAC_SIZE; use ironfish::IncomingViewKey; +use ironfish::MerkleNote; use ironfish::MerkleNoteHash; +use ironfish::Note; use ironfish::OutgoingViewKey; use napi::bindgen_prelude::*; use napi::JsBuffer; use napi_derive::napi; -use ironfish::merkle_note::NOTE_ENCRYPTION_KEY_SIZE; -use ironfish::note::ENCRYPTED_NOTE_SIZE; -use ironfish::serializing::aead::MAC_SIZE; -use ironfish::MerkleNote; - -use crate::to_napi_err; - #[napi] pub const NOTE_ENCRYPTION_KEY_LENGTH: u32 = NOTE_ENCRYPTION_KEY_SIZE as u32; @@ -29,6 +29,33 @@ pub const ENCRYPTED_NOTE_PLAINTEXT_LENGTH: u32 = ENCRYPTED_NOTE_SIZE as u32 + MA pub const ENCRYPTED_NOTE_LENGTH: u32 = NOTE_ENCRYPTION_KEY_LENGTH + ENCRYPTED_NOTE_PLAINTEXT_LENGTH + 96; +#[inline] +fn try_map(items: I, f: F) -> std::result::Result, E> +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + F: Fn(T) -> std::result::Result, +{ + let items = items.into_iter(); + let mut result = Vec::with_capacity(items.len()); + for item in items { + result.push(f(item)?); + } + Ok(result) +} + +#[inline] +fn decrypted_note_to_buffer(note: std::result::Result) -> Result> { + match note { + Ok(note) => { + let mut buf = [0u8; PLAINTEXT_NOTE_SIZE]; + note.write(&mut buf[..]).map_err(to_napi_err)?; + Ok(Some(Buffer::from(&buf[..]))) + } + Err(_) => Ok(None), + } +} + #[napi(js_name = "NoteEncrypted")] pub struct NativeNoteEncrypted { pub(crate) note: MerkleNote, @@ -110,15 +137,21 @@ impl NativeNoteEncrypted { pub fn decrypt_note_for_owner(&self, incoming_hex_key: String) -> Result> { let incoming_view_key = IncomingViewKey::from_hex(&incoming_hex_key).map_err(to_napi_err)?; + let decrypted_note = self.note.decrypt_note_for_owner(&incoming_view_key); + decrypted_note_to_buffer(decrypted_note).map_err(to_napi_err) + } - Ok(match self.note.decrypt_note_for_owner(&incoming_view_key) { - Ok(note) => { - let mut vec = vec![]; - note.write(&mut vec).map_err(to_napi_err)?; - Some(Buffer::from(vec)) - } - Err(_) => None, + #[napi] + pub fn decrypt_note_for_owners( + &self, + incoming_hex_keys: Vec, + ) -> Result>> { + let incoming_view_keys = try_map(&incoming_hex_keys[..], |hex_key| { + IncomingViewKey::from_hex(hex_key) }) + .map_err(to_napi_err)?; + let decrypted_notes = self.note.decrypt_note_for_owners(&incoming_view_keys); + try_map(decrypted_notes, decrypted_note_to_buffer).map_err(to_napi_err) } /// Returns undefined if the note was unable to be decrypted with the given key. @@ -126,15 +159,7 @@ impl NativeNoteEncrypted { pub fn decrypt_note_for_spender(&self, outgoing_hex_key: String) -> Result> { let outgoing_view_key = OutgoingViewKey::from_hex(&outgoing_hex_key).map_err(to_napi_err)?; - Ok( - match self.note.decrypt_note_for_spender(&outgoing_view_key) { - Ok(note) => { - let mut vec = vec![]; - note.write(&mut vec).map_err(to_napi_err)?; - Some(Buffer::from(vec)) - } - Err(_) => None, - }, - ) + let decrypted_note = self.note.decrypt_note_for_spender(&outgoing_view_key); + decrypted_note_to_buffer(decrypted_note).map_err(to_napi_err) } } diff --git a/ironfish-rust/Cargo.toml b/ironfish-rust/Cargo.toml index e3969bccbf..1f5f52f9d1 100644 --- a/ironfish-rust/Cargo.toml +++ b/ironfish-rust/Cargo.toml @@ -46,7 +46,7 @@ group = "0.12.0" ironfish-frost = { git = "https://github.com/iron-fish/ironfish-frost.git", branch = "main" } fish_hash = "0.3.0" ironfish_zkp = { version = "0.2.0", path = "../ironfish-zkp" } -jubjub = { git = "https://github.com/iron-fish/jubjub.git", branch = "blstrs" } +jubjub = { git = "https://github.com/iron-fish/jubjub.git", branch = "blstrs", features = ["multiply-many"] } lazy_static = "1.4.0" libc = "0.2.126" # sub-dependency that needs a pinned version until a new release of cpufeatures: https://github.com/RustCrypto/utils/pull/789 rand = "0.8.5" diff --git a/ironfish-rust/src/keys/view_keys.rs b/ironfish-rust/src/keys/view_keys.rs index f26bcdfc3f..0c32d90299 100644 --- a/ironfish-rust/src/keys/view_keys.rs +++ b/ironfish-rust/src/keys/view_keys.rs @@ -85,6 +85,21 @@ impl IncomingViewKey { pub(crate) fn shared_secret(&self, ephemeral_public_key: &SubgroupPoint) -> [u8; 32] { shared_secret(&self.view_key, ephemeral_public_key, ephemeral_public_key) } + + pub(crate) fn shared_secrets( + slice: &[Self], + ephemeral_public_key: &SubgroupPoint, + ) -> Vec<[u8; 32]> { + let raw_view_keys = slice + .iter() + .map(move |ivk| ivk.view_key.to_bytes()) + .collect::>(); + shared_secrets( + &raw_view_keys[..], + ephemeral_public_key, + ephemeral_public_key, + ) + } } /// Contains two keys that are required (along with outgoing view key) @@ -217,12 +232,36 @@ impl OutgoingViewKey { /// key (Bob's public key) to get the final shared secret /// /// The resulting key can be used in any symmetric cipher +#[must_use] pub(crate) fn shared_secret( secret_key: &jubjub::Fr, other_public_key: &SubgroupPoint, reference_public_key: &SubgroupPoint, ) -> [u8; 32] { let shared_secret = (other_public_key * secret_key).to_bytes(); + hash_shared_secret(&shared_secret, reference_public_key) +} + +/// Equivalent to calling `shared_secret()` multiple times on the same +/// `other_public_key`/`reference_public_key`, but more efficient. +#[must_use] +pub(crate) fn shared_secrets( + secret_keys: &[[u8; 32]], + other_public_key: &SubgroupPoint, + reference_public_key: &SubgroupPoint, +) -> Vec<[u8; 32]> { + let shared_secrets = other_public_key.as_extended().multiply_many(secret_keys); + shared_secrets + .into_iter() + .map(move |shared_secret| { + hash_shared_secret(&shared_secret.to_bytes(), reference_public_key) + }) + .collect() +} + +#[inline] +#[must_use] +fn hash_shared_secret(shared_secret: &[u8; 32], reference_public_key: &SubgroupPoint) -> [u8; 32] { let reference_bytes = reference_public_key.to_bytes(); let mut hasher = Blake2b::new() @@ -230,10 +269,11 @@ pub(crate) fn shared_secret( .personal(DIFFIE_HELLMAN_PERSONALIZATION) .to_state(); - hasher.update(&shared_secret); + hasher.update(&shared_secret[..]); hasher.update(&reference_bytes); + let mut hash_result = [0; 32]; - hash_result[..].clone_from_slice(hasher.finalize().as_ref()); + hash_result[..].copy_from_slice(hasher.finalize().as_ref()); hash_result } diff --git a/ironfish-rust/src/merkle_note.rs b/ironfish-rust/src/merkle_note.rs index e88aaa434b..be1d763d60 100644 --- a/ironfish-rust/src/merkle_note.rs +++ b/ironfish-rust/src/merkle_note.rs @@ -212,7 +212,7 @@ impl MerkleNote { owner_view_key: &IncomingViewKey, ) -> Result { #[cfg(feature = "note-encryption-stats")] - stats::inc_decrypt_note_for_owner(); + stats::inc_decrypt_note_for_owner(1); let shared_secret = owner_view_key.shared_secret(&self.ephemeral_public_key); let note = @@ -220,10 +220,40 @@ impl MerkleNote { note.verify_commitment(self.note_commitment)?; #[cfg(feature = "note-encryption-stats")] - stats::inc_decrypt_note_for_owner_ok(); + stats::inc_decrypt_note_for_owner_ok(1); Ok(note) } + pub fn decrypt_note_for_owners( + &self, + owner_view_keys: &[IncomingViewKey], + ) -> Vec> { + #[cfg(feature = "note-encryption-stats")] + stats::inc_decrypt_note_for_owner(owner_view_keys.len()); + + let shared_secrets = + IncomingViewKey::shared_secrets(owner_view_keys, &self.ephemeral_public_key); + + let result = owner_view_keys + .iter() + .zip(shared_secrets.iter()) + .map(move |(owner_view_key, shared_secret)| { + let note = Note::from_owner_encrypted( + owner_view_key, + shared_secret, + &self.encrypted_note, + )?; + note.verify_commitment(self.note_commitment)?; + Ok(note) + }) + .collect::>>(); + + #[cfg(feature = "note-encryption-stats")] + stats::inc_decrypt_note_for_owner_ok(result.iter().filter(move |res| res.is_ok()).count()); + + result + } + pub fn decrypt_note_for_spender( &self, spender_key: &OutgoingViewKey, @@ -353,13 +383,13 @@ pub mod stats { } #[inline(always)] - pub(super) fn inc_decrypt_note_for_owner() { - DECRYPT_FOR_OWNER_CALLS.fetch_add(1, Relaxed); + pub(super) fn inc_decrypt_note_for_owner(count: usize) { + DECRYPT_FOR_OWNER_CALLS.fetch_add(count, Relaxed); } #[inline(always)] - pub(super) fn inc_decrypt_note_for_owner_ok() { - DECRYPT_FOR_OWNER_OK_CALLS.fetch_add(1, Relaxed); + pub(super) fn inc_decrypt_note_for_owner_ok(count: usize) { + DECRYPT_FOR_OWNER_OK_CALLS.fetch_add(count, Relaxed); } #[inline(always)] diff --git a/ironfish-rust/src/note.rs b/ironfish-rust/src/note.rs index 912398a690..3a87c09ce4 100644 --- a/ironfish-rust/src/note.rs +++ b/ironfish-rust/src/note.rs @@ -35,6 +35,12 @@ pub const ENCRYPTED_NOTE_SIZE: usize = // + 32 memo // + 32 sender address // = 136 +pub const PLAINTEXT_NOTE_SIZE: usize = PUBLIC_ADDRESS_SIZE + + ASSET_ID_LENGTH + + AMOUNT_VALUE_SIZE + + SCALAR_SIZE + + MEMO_SIZE + + PUBLIC_ADDRESS_SIZE; pub const SCALAR_SIZE: usize = 32; pub const MEMO_SIZE: usize = 32; pub const AMOUNT_VALUE_SIZE: usize = 8; @@ -105,7 +111,7 @@ pub struct Note { pub(crate) sender: PublicAddress, } -impl<'a> Note { +impl Note { /// Construct a new Note. pub fn new( owner: PublicAddress, @@ -158,7 +164,7 @@ impl<'a> Note { /// This should generally never be used to serialize to disk or the network. /// It is primarily added as a device for transmitting the note across /// thread boundaries. - pub fn write(&self, mut writer: &mut W) -> Result<(), IronfishError> { + pub fn write(&self, mut writer: W) -> Result<(), IronfishError> { self.owner.write(&mut writer)?; self.asset_id.write(&mut writer)?; writer.write_u64::(self.value)?; @@ -179,7 +185,7 @@ impl<'a> Note { /// This function allows the owner to decrypt the note using the derived /// shared secret and their own view key. pub fn from_owner_encrypted( - owner_view_key: &'a IncomingViewKey, + owner_view_key: &IncomingViewKey, shared_secret: &[u8; 32], encrypted_bytes: &[u8; ENCRYPTED_NOTE_SIZE + aead::MAC_SIZE], ) -> Result { @@ -349,10 +355,9 @@ impl<'a> Note { ) -> Result<(jubjub::Fr, AssetIdentifier, u64, Memo, PublicAddress), IronfishError> { let plaintext_bytes: [u8; ENCRYPTED_NOTE_SIZE] = aead::decrypt(shared_secret, encrypted_bytes)?; + let mut reader = &plaintext_bytes[..]; - let mut reader = plaintext_bytes[..].as_ref(); - - let randomness: jubjub::Fr = read_scalar(&mut reader)?; + let randomness = read_scalar(&mut reader)?; let value = reader.read_u64::()?; let mut memo = Memo::default(); diff --git a/ironfish/src/primitives/noteEncrypted.ts b/ironfish/src/primitives/noteEncrypted.ts index 8a58c30395..dd4d39921f 100644 --- a/ironfish/src/primitives/noteEncrypted.ts +++ b/ironfish/src/primitives/noteEncrypted.ts @@ -89,6 +89,18 @@ export class NoteEncrypted { } } + decryptNoteForOwners(ownerHexKeys: Array): Array { + if (ownerHexKeys.length === 0) { + return [] + } else if (ownerHexKeys.length === 1) { + return [this.decryptNoteForOwner(ownerHexKeys[0])] + } + + const notes = this.takeReference().decryptNoteForOwners(ownerHexKeys) + this.returnReference() + return notes.map((note) => (note ? new Note(note) : undefined)) + } + decryptNoteForSpender(spenderHexKey: string): Note | undefined { const note = this.takeReference().decryptNoteForSpender(spenderHexKey) this.returnReference() diff --git a/ironfish/src/workerPool/tasks/decryptNotes.ts b/ironfish/src/workerPool/tasks/decryptNotes.ts index 033e5c92e8..615474e3a8 100644 --- a/ironfish/src/workerPool/tasks/decryptNotes.ts +++ b/ironfish/src/workerPool/tasks/decryptNotes.ts @@ -285,10 +285,14 @@ export class DecryptNotesTask extends WorkerTask { skipValidation: options.skipNoteValidation, }) - for (const { incomingViewKey, outgoingViewKey, viewKey } of accountKeys) { + const receivedNotes = note.decryptNoteForOwners( + accountKeys.map(({ incomingViewKey }) => incomingViewKey), + ) + + for (const [i, receivedNote] of receivedNotes.entries()) { // Try decrypting the note as the owner - const receivedNote = note.decryptNoteForOwner(incomingViewKey) if (receivedNote && receivedNote.value() !== 0n) { + const { viewKey } = accountKeys[i] decryptedNotes.push({ index: currentNoteIndex, forSpender: false, @@ -304,6 +308,7 @@ export class DecryptNotesTask extends WorkerTask { if (options.decryptForSpender) { // Try decrypting the note as the spender + const { outgoingViewKey } = accountKeys[i] const spentNote = note.decryptNoteForSpender(outgoingViewKey) if (spentNote && spentNote.value() !== 0n) { decryptedNotes.push({ diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index a025a80c4e..0ab61b20e5 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -63,7 +63,7 @@ delta = "1.9.3 -> 2.2.6" [[audits.jubjub]] who = "Andrea " criteria = "safe-to-deploy" -delta = "0.9.0 -> 0.9.0@git:6fac532975e23acd3fa670bc0383d027f09403e5" +delta = "0.9.0 -> 0.9.0@git:531157cfa7b81ade207e819ef50c563843b10e30" importable = false notes = "Fork of the official jubjub owned by Iron Fish" diff --git a/supply-chain/config.toml b/supply-chain/config.toml index af8d1e6797..52c74ea903 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -37,7 +37,7 @@ audit-as-crates-io = true [policy.ironfish_zkp] audit-as-crates-io = true -[policy."jubjub:0.9.0@git:6fac532975e23acd3fa670bc0383d027f09403e5"] +[policy."jubjub:0.9.0@git:531157cfa7b81ade207e819ef50c563843b10e30"] audit-as-crates-io = true [policy."reddsa:0.5.1@git:311baf8865f6e21527d1f20750d8f2cf5c9e531a"] From f6439a7420394f882cd68e54adc81d2526ffcb1c Mon Sep 17 00:00:00 2001 From: andrea Date: Tue, 2 Jul 2024 16:12:03 -0700 Subject: [PATCH 40/81] Remove `@types/jest`, and use Jest's own type annotations instead --- ironfish-cli/src/testHarness.ts | 25 ++++++++ .../src/utils/chainport/utils.test.ts | 7 +- ironfish-rust-nodejs/package.json | 1 - ironfish-rust-nodejs/tests/testHarness.ts | 25 ++++++++ ironfish/package.json | 1 - ironfish/src/assets/assetsVerifier.test.ts | 3 +- ironfish/src/chainProcessor.test.ts | 9 +-- ironfish/src/event.test.ts | 2 +- ironfish/src/genesis/genesis.test.slow.ts | 13 ++-- ironfish/src/memPool/memPool.test.ts | 4 +- ironfish/src/network/blockFetcher.test.ts | 8 ++- ironfish/src/network/peerNetwork.test.ts | 8 ++- .../peers/connections/connection.test.ts | 4 +- ironfish/src/network/peers/peer.test.ts | 2 +- .../src/network/peers/peerManager.test.ts | 18 ++++-- ironfish/src/network/testUtilities/helpers.ts | 3 +- .../src/network/transactionFetcher.test.ts | 2 +- .../src/rpc/routes/faucet/getFunds.test.ts | 12 ++-- ironfish/src/rpc/routes/node/stopNode.test.ts | 2 +- ironfish/src/sdk.test.ts | 4 +- ironfish/src/storage/database.test.ts | 28 ++++---- .../src/storage/database/encoding.test.ts | 24 +++---- ironfish/src/syncer.test.ts | 6 +- ironfish/src/telemetry/telemetry.test.ts | 4 +- ironfish/src/testHarness.ts | 25 ++++++++ .../src/testUtilities/matchers/blockchain.ts | 27 ++++---- ironfish/src/testUtilities/matchers/buffer.ts | 12 ++-- .../src/testUtilities/matchers/merkletree.ts | 24 +++---- ironfish/src/testUtilities/matchers/string.ts | 12 ++-- ironfish/src/testUtilities/mocks.ts | 4 +- ironfish/src/testUtilities/utils.test.ts | 53 --------------- ironfish/src/testUtilities/utils.ts | 64 ------------------- ironfish/src/utils/retry.test.ts | 6 +- .../src/wallet/scanner/noteDecryptor.test.ts | 5 +- .../scanner/remoteChainProcessor.test.ts | 5 +- ironfish/src/wallet/wallet.test.slow.ts | 5 +- ironfish/src/wallet/wallet.test.ts | 2 +- .../workerPool/tasks/submitTelemetry.test.ts | 4 +- package.json | 1 - yarn.lock | 58 +---------------- 40 files changed, 220 insertions(+), 302 deletions(-) create mode 100644 ironfish-cli/src/testHarness.ts create mode 100644 ironfish-rust-nodejs/tests/testHarness.ts create mode 100644 ironfish/src/testHarness.ts delete mode 100644 ironfish/src/testUtilities/utils.test.ts diff --git a/ironfish-cli/src/testHarness.ts b/ironfish-cli/src/testHarness.ts new file mode 100644 index 0000000000..f89eb8062d --- /dev/null +++ b/ironfish-cli/src/testHarness.ts @@ -0,0 +1,25 @@ +/* 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 type { Jest } from '@jest/environment' +import type { JestExpect } from '@jest/expect' +import type { Global } from '@jest/types' + +declare global { + const { + it, + test, + fit, + xit, + xtest, + describe, + xdescribe, + fdescribe, + beforeAll, + beforeEach, + afterEach, + afterAll, + }: Global.GlobalAdditions + const expect: JestExpect + const jest: Jest +} diff --git a/ironfish-cli/src/utils/chainport/utils.test.ts b/ironfish-cli/src/utils/chainport/utils.test.ts index 53582df2e6..74ef594fef 100644 --- a/ironfish-cli/src/utils/chainport/utils.test.ts +++ b/ironfish-cli/src/utils/chainport/utils.test.ts @@ -1,6 +1,7 @@ /* 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 type { Mock } from 'jest-mock' import { RpcWalletNote, RpcWalletTransaction, TransactionType } from '@ironfish/sdk' import { getConfig } from './config' import { ChainportMemoMetadata } from './metadata' @@ -16,7 +17,7 @@ describe('isChainportTransaction', () => { } beforeEach(() => { - ;(getConfig as jest.Mock).mockReturnValue(mockConfig) + ;(getConfig as Mock).mockReturnValue(mockConfig) }) it('should return false for non-SEND/RECEIVE transactions', () => { @@ -42,7 +43,7 @@ describe('isChainportTransaction', () => { }) it('should return true for valid incoming chainport transaction', () => { - ;(ChainportMemoMetadata.decode as jest.Mock).mockReturnValue([1, 'address']) + ;(ChainportMemoMetadata.decode as Mock).mockReturnValue([1, 'address']) const transaction = { type: TransactionType.RECEIVE, @@ -92,7 +93,7 @@ describe('isChainportTransaction', () => { }) it('should return true for valid outgoing chainport transaction', () => { - ;(ChainportMemoMetadata.decode as jest.Mock).mockReturnValue([1, 'address']) + ;(ChainportMemoMetadata.decode as Mock).mockReturnValue([1, 'address']) const transaction = { type: TransactionType.SEND, notes: [ diff --git a/ironfish-rust-nodejs/package.json b/ironfish-rust-nodejs/package.json index 3b75843e3b..8282c3ef0c 100644 --- a/ironfish-rust-nodejs/package.json +++ b/ironfish-rust-nodejs/package.json @@ -35,7 +35,6 @@ }, "devDependencies": { "@napi-rs/cli": "2.16.1", - "@types/jest": "29.5.8", "jest": "29.7.0", "rimraf": "3.0.2", "ts-jest": "29.1.1", diff --git a/ironfish-rust-nodejs/tests/testHarness.ts b/ironfish-rust-nodejs/tests/testHarness.ts new file mode 100644 index 0000000000..f89eb8062d --- /dev/null +++ b/ironfish-rust-nodejs/tests/testHarness.ts @@ -0,0 +1,25 @@ +/* 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 type { Jest } from '@jest/environment' +import type { JestExpect } from '@jest/expect' +import type { Global } from '@jest/types' + +declare global { + const { + it, + test, + fit, + xit, + xtest, + describe, + xdescribe, + fdescribe, + beforeAll, + beforeEach, + afterEach, + afterAll, + }: Global.GlobalAdditions + const expect: JestExpect + const jest: Jest +} diff --git a/ironfish/package.json b/ironfish/package.json index ac067139f2..e5dae413cb 100644 --- a/ironfish/package.json +++ b/ironfish/package.json @@ -71,7 +71,6 @@ "@types/buffer-json": "2.0.0", "@types/colors": "1.2.1", "@types/imurmurhash": "0.1.1", - "@types/jest": "29.5.8", "@types/leveldown": "4.0.2", "@types/levelup": "4.3.0", "@types/lodash": "4.14.170", diff --git a/ironfish/src/assets/assetsVerifier.test.ts b/ironfish/src/assets/assetsVerifier.test.ts index e6bdcf273a..83b182d16a 100644 --- a/ironfish/src/assets/assetsVerifier.test.ts +++ b/ironfish/src/assets/assetsVerifier.test.ts @@ -1,6 +1,7 @@ /* 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 type { SpyInstance } from 'jest-mock' import nock from 'nock' import { VerifiedAssetsCacheStore } from '../fileStores/verifiedAssets' import { NodeFileProvider } from '../fileSystems' @@ -35,7 +36,7 @@ const assetData3 = { describe('AssetsVerifier', () => { jest.useFakeTimers() - const waitForRefreshToFinish = async (refreshSpy: jest.SpyInstance) => { + const waitForRefreshToFinish = async (refreshSpy: SpyInstance) => { for (const result of refreshSpy.mock.results) { await result.value } diff --git a/ironfish/src/chainProcessor.test.ts b/ironfish/src/chainProcessor.test.ts index 5b230a7b7c..e9b614bfe0 100644 --- a/ironfish/src/chainProcessor.test.ts +++ b/ironfish/src/chainProcessor.test.ts @@ -2,6 +2,7 @@ * 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 type { Mock } from 'jest-mock' import { ChainProcessor } from './chainProcessor' import { BlockHeader } from './primitives/blockheader' import { createNodeTest, useMinerBlockFixture } from './testUtilities' @@ -32,7 +33,7 @@ describe('ChainProcessor', () => { head: chain.genesis.hash, }) - const onEvent: jest.Mock = jest.fn() + const onEvent: Mock<(header: BlockHeader, event: 'add' | 'remove') => void> = jest.fn() processor.onAdd.on((block) => onEvent(block, 'add')) processor.onRemove.on((block) => onEvent(block, 'remove')) @@ -82,7 +83,7 @@ describe('ChainProcessor', () => { head: chain.genesis.hash, }) - const onEvent: jest.Mock = jest.fn() + const onEvent: Mock<(header: BlockHeader, event: 'add' | 'remove') => void> = jest.fn() processor.onAdd.on((block) => onEvent(block, 'add')) processor.onRemove.on((block) => onEvent(block, 'remove')) @@ -149,7 +150,7 @@ describe('ChainProcessor', () => { expect(chain.head.hash).toEqual(block2.header.hash) - const onEvent: jest.Mock = jest.fn() + const onEvent: Mock<(header: BlockHeader, event: 'add' | 'remove') => void> = jest.fn() const processor = new ChainProcessor({ chain, head: null }) processor.onAdd.on((block) => onEvent(block, 'add')) @@ -178,7 +179,7 @@ describe('ChainProcessor', () => { await expect(chain).toAddBlock(block) expect(chain.head.hash).toEqual(block.header.hash) - const onEvent: jest.Mock = jest.fn() + const onEvent: Mock<(header: BlockHeader, event: 'add' | 'remove') => void> = jest.fn() const processor = new ChainProcessor({ chain, head: chain.genesis.hash }) processor.onAdd.on((block) => onEvent(block, 'add')) diff --git a/ironfish/src/event.test.ts b/ironfish/src/event.test.ts index 6b11f1341b..97abdd7869 100644 --- a/ironfish/src/event.test.ts +++ b/ironfish/src/event.test.ts @@ -44,7 +44,7 @@ describe('Event', () => { it('should remove once', () => { const event = new Event<[]>() - const mock = jest.fn() + const mock = jest.fn<() => void>() event.once(mock) diff --git a/ironfish/src/genesis/genesis.test.slow.ts b/ironfish/src/genesis/genesis.test.slow.ts index 7868b63df8..0dee91aaf6 100644 --- a/ironfish/src/genesis/genesis.test.slow.ts +++ b/ironfish/src/genesis/genesis.test.slow.ts @@ -1,6 +1,7 @@ /* 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 type { SpiedFunction } from 'jest-mock' import { Asset, generateKey } from '@ironfish/rust-nodejs' import { BlockSerde, SerializedBlock } from '../primitives/block' import { Target } from '../primitives/target' @@ -13,8 +14,8 @@ import { GenesisBlockInfo, makeGenesisBlock } from './makeGenesisBlock' describe('Read genesis block', () => { const nodeTest = createNodeTest() - let targetMeetsSpy: jest.SpyInstance - let targetSpy: jest.SpyInstance + let targetMeetsSpy: SpiedFunction + let targetSpy: SpiedFunction beforeAll(() => { targetMeetsSpy = jest.spyOn(Target, 'meets').mockImplementation(() => true) @@ -41,8 +42,8 @@ describe('Read genesis block', () => { describe('Create genesis block', () => { const nodeTest = createNodeTest(false, { autoSeed: false }) - let targetMeetsSpy: jest.SpyInstance - let targetSpy: jest.SpyInstance + let targetMeetsSpy: SpiedFunction + let targetSpy: SpiedFunction beforeAll(() => { targetMeetsSpy = jest.spyOn(Target, 'meets').mockImplementation(() => true) @@ -151,8 +152,8 @@ describe('Create genesis block', () => { describe('addGenesisTransaction', () => { const nodeTest = createNodeTest(false, { autoSeed: false }) - let targetMeetsSpy: jest.SpyInstance - let targetSpy: jest.SpyInstance + let targetMeetsSpy: SpiedFunction + let targetSpy: SpiedFunction beforeAll(() => { targetMeetsSpy = jest.spyOn(Target, 'meets').mockImplementation(() => true) diff --git a/ironfish/src/memPool/memPool.test.ts b/ironfish/src/memPool/memPool.test.ts index d1ecac23b6..1634f838d2 100644 --- a/ironfish/src/memPool/memPool.test.ts +++ b/ironfish/src/memPool/memPool.test.ts @@ -277,7 +277,9 @@ describe('MemPool', () => { describe('with an expired sequence', () => { const nodeTest = createNodeTest() - afterEach(() => jest.restoreAllMocks()) + afterEach(() => { + jest.restoreAllMocks() + }) it('returns false', async () => { const { node } = nodeTest diff --git a/ironfish/src/network/blockFetcher.test.ts b/ironfish/src/network/blockFetcher.test.ts index 8231b412c5..3854951bff 100644 --- a/ironfish/src/network/blockFetcher.test.ts +++ b/ironfish/src/network/blockFetcher.test.ts @@ -71,7 +71,7 @@ describe('BlockFetcher', () => { expect(sentPeers).toHaveLength(1) expect(sentPeers[0].sendSpy).toHaveBeenCalledWith( - new GetCompactBlockRequest(hash, expect.any(Number)), + new GetCompactBlockRequest(hash, expect.any(Number) as unknown as number), ) await peerNetwork.stop() @@ -177,7 +177,11 @@ describe('BlockFetcher', () => { expect(peers[0].sendSpy.mock.calls).toHaveLength(1) const request = peers[0].sendSpy.mock.calls[0][0] expect(request).toEqual( - new GetBlockTransactionsRequest(block.header.hash, [1, 0, 1, 0], expect.any(Number)), + new GetBlockTransactionsRequest( + block.header.hash, + [1, 0, 1, 0], + expect.any(Number) as unknown as number, + ), ) expect(await chain.hasBlock(block.header.hash)).toBe(false) diff --git a/ironfish/src/network/peerNetwork.test.ts b/ironfish/src/network/peerNetwork.test.ts index 10d93bbcda..8d91865ccc 100644 --- a/ironfish/src/network/peerNetwork.test.ts +++ b/ironfish/src/network/peerNetwork.test.ts @@ -111,7 +111,7 @@ describe('PeerNetwork', () => { expect(peerNetwork.isReady).toBe(false) - const readyChanged = jest.fn() + const readyChanged = jest.fn<(ready: boolean) => void>() peerNetwork.onIsReadyChanged.on(readyChanged) peerNetwork.start() @@ -893,7 +893,8 @@ describe('PeerNetwork', () => { // Don't sync incoming transactions to wallet since its done async and will // attempt to update the wallet after the test has finished peerNetwork.onTransactionGossipReceived.clear() - const onTransactionGossipReceivedSpy = jest.fn() + const onTransactionGossipReceivedSpy = + jest.fn<(transaction: Transaction, valid: boolean) => void>() peerNetwork.onTransactionGossipReceived.on(onTransactionGossipReceivedSpy) await peerNetwork.peerManager.onMessage.emitAsync( @@ -1161,7 +1162,8 @@ describe('PeerNetwork', () => { // Don't sync incoming transactions to wallet since its done async and will // attempt to update the wallet after the test has finished peerNetwork.onTransactionGossipReceived.clear() - const onTransactionGossipReceivedSpy = jest.fn() + const onTransactionGossipReceivedSpy = + jest.fn<(transaction: Transaction, valid: boolean) => void>() peerNetwork.onTransactionGossipReceived.on(onTransactionGossipReceivedSpy) await peerNetwork.peerManager.onMessage.emitAsync( diff --git a/ironfish/src/network/peers/connections/connection.test.ts b/ironfish/src/network/peers/connections/connection.test.ts index ee0cb72c85..6ffa6d9663 100644 --- a/ironfish/src/network/peers/connections/connection.test.ts +++ b/ironfish/src/network/peers/connections/connection.test.ts @@ -71,7 +71,9 @@ describe('Connection', () => { features: defaultFeatures(), }) - const _sendSpy = jest.spyOn(connection, '_send').mockImplementationOnce(jest.fn()) + const _sendSpy = jest + .spyOn(connection, '_send') + .mockImplementationOnce(jest.fn<(data: Buffer) => boolean>()) expect(connection.send(message)).toBe(false) expect(_sendSpy).not.toHaveBeenCalled() diff --git a/ironfish/src/network/peers/peer.test.ts b/ironfish/src/network/peers/peer.test.ts index ebcc9caa4a..67b4b38be8 100644 --- a/ironfish/src/network/peers/peer.test.ts +++ b/ironfish/src/network/peers/peer.test.ts @@ -358,7 +358,7 @@ describe('punish', () => { connections: { webSocket: connection }, }) - const onBannedHandler = jest.fn() + const onBannedHandler = jest.fn<(reason: string) => void>() peer.onBanned.on(onBannedHandler) peer.punish(BAN_SCORE.MAX, 'TESTING') diff --git a/ironfish/src/network/peers/peerManager.test.ts b/ironfish/src/network/peers/peerManager.test.ts index 69ad2a5203..7e8ab92f87 100644 --- a/ironfish/src/network/peers/peerManager.test.ts +++ b/ironfish/src/network/peers/peerManager.test.ts @@ -9,6 +9,7 @@ import { Assert } from '../../assert' import { canInitiateWebRTC, privateIdentityToIdentity } from '../identity' import { DisconnectingMessage, DisconnectingReason } from '../messages/disconnecting' import { IdentifyMessage } from '../messages/identify' +import { NetworkMessage } from '../messages/networkMessage' import { PeerListMessage } from '../messages/peerList' import { PeerListRequestMessage } from '../messages/peerListRequest' import { SignalMessage } from '../messages/signal' @@ -30,12 +31,13 @@ import { NetworkMessageType } from '../types' import { formatWebSocketAddress } from '../utils' import { VERSION_PROTOCOL, VERSION_PROTOCOL_MIN } from '../version' import { + Connection, ConnectionDirection, ConnectionType, WebRtcConnection, WebSocketConnection, } from './connections' -import { BAN_SCORE } from './peer' +import { BAN_SCORE, Peer } from './peer' import { defaultFeatures } from './peerFeatures' import { PeerManager } from './peerManager' @@ -495,7 +497,7 @@ describe('PeerManager', () => { // Create the peer to broker the connection through const { peer: brokeringPeer } = getConnectedPeer(peers) - const brokerPeerSendMock = jest.fn() + const brokerPeerSendMock = jest.fn<(message: NetworkMessage) => Connection | null>() brokeringPeer.send = brokerPeerSendMock // Create the peer to connect to WebRTC through @@ -733,7 +735,7 @@ describe('PeerManager', () => { it('Emits onConnectedPeersChanged when a peer enters CONNECTED or DISCONNECTED', () => { const pm = new PeerManager(mockLocalPeer(), mockPeerStore()) - const onConnectedPeersChangedMock = jest.fn() + const onConnectedPeersChangedMock = jest.fn<() => void>() pm.onConnectedPeersChanged.on(onConnectedPeersChangedMock) const { peer: connecting } = getConnectingPeer(pm) @@ -1118,7 +1120,8 @@ describe('PeerManager', () => { mockLocalPeer({ identity: webRtcLocalIdentity() }), mockPeerStore(), ) - const initWebRtcConnectionMock = jest.fn() + const initWebRtcConnectionMock = + jest.fn<(peer: Peer, initiator: boolean) => WebRtcConnection>() pm['initWebRtcConnection'] = initWebRtcConnectionMock const { peer, connection } = getConnectedPeer(pm, webRtcCannotInitiateIdentity()) @@ -1142,7 +1145,8 @@ describe('PeerManager', () => { mockLocalPeer({ identity: webRtcLocalIdentity() }), mockPeerStore(), ) - const initWebRtcConnectionMock = jest.fn() + const initWebRtcConnectionMock = + jest.fn<(peer: Peer, initiator: boolean) => WebRtcConnection>() pm['initWebRtcConnection'] = initWebRtcConnectionMock const { peer, connection } = getConnectedPeer(pm, webRtcCanInitiateIdentity()) @@ -1184,7 +1188,7 @@ describe('PeerManager', () => { peer1.onMessage.emit(message, peer1Connection) const reply = new DisconnectingMessage({ - disconnectUntil: expect.any(Number), + disconnectUntil: expect.any(Number) as unknown as number, reason: DisconnectingReason.Congested, sourceIdentity: pm.localPeer.publicIdentity, destinationIdentity: webRtcCanInitiateIdentity(), @@ -1286,7 +1290,7 @@ describe('PeerManager', () => { peer1.onMessage.emit(message, peer1Connection) const reply = new DisconnectingMessage({ - disconnectUntil: expect.any(Number), + disconnectUntil: expect.any(Number) as unknown as number, reason: DisconnectingReason.Congested, sourceIdentity: pm.localPeer.publicIdentity, destinationIdentity: webRtcCannotInitiateIdentity(), diff --git a/ironfish/src/network/testUtilities/helpers.ts b/ironfish/src/network/testUtilities/helpers.ts index 5aad6a6622..06bfc7d9f1 100644 --- a/ironfish/src/network/testUtilities/helpers.ts +++ b/ironfish/src/network/testUtilities/helpers.ts @@ -2,6 +2,7 @@ * 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 type { SpiedFunction } from 'jest-mock' import { Assert } from '../../assert' import { Identity, isIdentity } from '../identity' import { GetBlockHeadersResponse } from '../messages/getBlockHeaders' @@ -91,7 +92,7 @@ export const getConnectedPeersWithSpies = ( count: number, ): { peer: Peer - sendSpy: jest.SpyInstance + sendSpy: SpiedFunction<(message: NetworkMessage) => Connection | null> }[] => { return [...Array(count)].map((_) => { const { peer } = getConnectedPeer(peerManager) diff --git a/ironfish/src/network/transactionFetcher.test.ts b/ironfish/src/network/transactionFetcher.test.ts index 86d906eea5..6e3d96ce48 100644 --- a/ironfish/src/network/transactionFetcher.test.ts +++ b/ironfish/src/network/transactionFetcher.test.ts @@ -51,7 +51,7 @@ describe('TransactionFetcher', () => { expect(sentPeers).toHaveLength(1) expect(sentPeers[0].sendSpy).toHaveBeenCalledWith( - new PooledTransactionsRequest([hash], expect.any(Number)), + new PooledTransactionsRequest([hash], expect.any(Number) as unknown as number), ) await peerNetwork.stop() diff --git a/ironfish/src/rpc/routes/faucet/getFunds.test.ts b/ironfish/src/rpc/routes/faucet/getFunds.test.ts index df055379ca..8eab7589a0 100644 --- a/ironfish/src/rpc/routes/faucet/getFunds.test.ts +++ b/ironfish/src/rpc/routes/faucet/getFunds.test.ts @@ -25,7 +25,9 @@ describe('Route faucet.getFunds', () => { it('returns a 200 status code', async () => { routeTest.node.config.set('getFundsApi', 'foo.com') - axios.post = jest.fn().mockImplementationOnce(() => Promise.resolve({ data: { id: 5 } })) + axios.post = jest + .fn() + .mockResolvedValueOnce({ data: { id: 5 } }) as typeof axios.post const response = await routeTest.client .request('faucet/getFunds', { @@ -46,7 +48,7 @@ describe('Route faucet.getFunds', () => { describe('when too many faucet requests have been made', () => { it('throws an error', async () => { - axios.post = jest.fn().mockImplementationOnce(() => { + axios.post = jest.fn().mockImplementationOnce(() => { throw { response: { data: { @@ -55,7 +57,7 @@ describe('Route faucet.getFunds', () => { }, }, } - }) + }) as typeof axios.post await expect( routeTest.client.faucet.getFunds({ account: accountName, email }), ).rejects.toThrow(RpcRequestError) @@ -65,7 +67,9 @@ describe('Route faucet.getFunds', () => { describe('when the API request fails', () => { it('throws an error', async () => { const apiResponse = new Error('API failure') as AxiosError - axios.post = jest.fn().mockRejectedValueOnce(apiResponse) + axios.post = jest + .fn() + .mockRejectedValueOnce(apiResponse) as typeof axios.post await expect( routeTest.client.faucet.getFunds({ account: accountName, email }), ).rejects.toThrow('API failure') diff --git a/ironfish/src/rpc/routes/node/stopNode.test.ts b/ironfish/src/rpc/routes/node/stopNode.test.ts index c583f84a88..00107f4d49 100644 --- a/ironfish/src/rpc/routes/node/stopNode.test.ts +++ b/ironfish/src/rpc/routes/node/stopNode.test.ts @@ -7,7 +7,7 @@ describe('Route node.getStatus', () => { const routeTest = createRouteTest() it('should get status', async () => { - routeTest.node.shutdown = jest.fn() + routeTest.node.shutdown = jest.fn<() => Promise>() const response = await routeTest.client.node.stopNode() expect(response.status).toBe(200) diff --git a/ironfish/src/sdk.test.ts b/ironfish/src/sdk.test.ts index f8d5900d7b..a12f52b19c 100644 --- a/ironfish/src/sdk.test.ts +++ b/ironfish/src/sdk.test.ts @@ -285,7 +285,7 @@ describe('IronfishSdk', () => { expect(connect).toHaveBeenCalledTimes(1) expect(client).toBeInstanceOf(RpcIpcClient) - expect(client).toMatchObject(sdk.client) + expect(client).toEqual(sdk.client) }) }) @@ -303,7 +303,7 @@ describe('IronfishSdk', () => { expect(connect).toHaveBeenCalledTimes(1) expect(client).toBeInstanceOf(RpcTcpClient) - expect(client).toMatchObject(sdk.client) + expect(client).toEqual(sdk.client) }) }) }) diff --git a/ironfish/src/storage/database.test.ts b/ironfish/src/storage/database.test.ts index 4da8396fbf..0935bf7e98 100644 --- a/ironfish/src/storage/database.test.ts +++ b/ironfish/src/storage/database.test.ts @@ -167,9 +167,9 @@ describe('Database', () => { const clearRange = StorageUtils.getPrefixKeyRange(Buffer.from('2')) - expect(await testStore.getAllKeys()).toMatchObject(['1', '2a', '2b', '3']) + expect(await testStore.getAllKeys()).toEqual(['1', '2a', '2b', '3']) await testStore.clear(undefined, clearRange) - expect(await testStore.getAllKeys()).toMatchObject(['1', '3']) + expect(await testStore.getAllKeys()).toEqual(['1', '3']) }) it('should clear store in a transaction', async () => { @@ -205,9 +205,9 @@ describe('Database', () => { const clearRange = StorageUtils.getPrefixKeyRange(Buffer.from('2')) - await expect(testStore.getAllKeys(tx)).resolves.toMatchObject(['1', '2a', '2b', '3']) + await expect(testStore.getAllKeys(tx)).resolves.toEqual(['1', '2a', '2b', '3']) await testStore.clear(tx, clearRange) - await expect(testStore.getAllKeys(tx)).resolves.toMatchObject(['1', '3']) + await expect(testStore.getAllKeys(tx)).resolves.toEqual(['1', '3']) }) }) @@ -617,8 +617,8 @@ describe('Database', () => { await db.metaStore.put('c', 1002) await db.metaStore.put('d', 1003) - await expect(db.metaStore.getAllKeys()).resolves.toMatchObject(['a', 'b', 'c', 'd']) - await expect(db.metaStore.getAllValues()).resolves.toMatchObject([1000, 1001, 1002, 1003]) + await expect(db.metaStore.getAllKeys()).resolves.toEqual(['a', 'b', 'c', 'd']) + await expect(db.metaStore.getAllValues()).resolves.toEqual([1000, 1001, 1002, 1003]) }) it('should get all keys and values in a range', async () => { @@ -635,14 +635,14 @@ describe('Database', () => { gte: Buffer.from('b'), lt: Buffer.from('d'), }), - ).resolves.toMatchObject([1001, 1002]) + ).resolves.toEqual([1001, 1002]) await expect( db.metaStore.getAllKeys(undefined, { gte: Buffer.from('b'), lt: Buffer.from('d'), }), - ).resolves.toMatchObject(['b', 'c']) + ).resolves.toEqual(['b', 'c']) }) it('should encode and decode keys', async () => { @@ -759,27 +759,27 @@ describe('Database', () => { await db.transaction(async (tx) => { await expect( db.metaStore.getAllKeys(tx, undefined, { ordered: true }), - ).resolves.toMatchObject(['a', 'b', 'd', 'e']) + ).resolves.toEqual(['a', 'b', 'd', 'e']) await expect( db.metaStore.getAllValues(tx, undefined, { ordered: true }), - ).resolves.toMatchObject([1001, 1003, 1002, 1000]) + ).resolves.toEqual([1001, 1003, 1002, 1000]) await db.metaStore.put('a', 1004, tx) await db.metaStore.put('c', 999, tx) await expect( db.metaStore.getAllKeys(tx, undefined, { ordered: true }), - ).resolves.toMatchObject(['a', 'b', 'c', 'd', 'e']) + ).resolves.toEqual(['a', 'b', 'c', 'd', 'e']) await expect( db.metaStore.getAllValues(tx, undefined, { ordered: true }), - ).resolves.toMatchObject([1004, 1003, 999, 1002, 1000]) + ).resolves.toEqual([1004, 1003, 999, 1002, 1000]) await expect( db.metaStore.getAllKeys(tx, undefined, { ordered: true, reverse: true }), - ).resolves.toMatchObject(['e', 'd', 'c', 'b', 'a']) + ).resolves.toEqual(['e', 'd', 'c', 'b', 'a']) await expect( db.metaStore.getAllValues(tx, undefined, { ordered: true, reverse: true }), - ).resolves.toMatchObject([1000, 1002, 999, 1003, 1004]) + ).resolves.toEqual([1000, 1002, 999, 1003, 1004]) }) }) }) diff --git a/ironfish/src/storage/database/encoding.test.ts b/ironfish/src/storage/database/encoding.test.ts index d1df25a8d9..bf208c074c 100644 --- a/ironfish/src/storage/database/encoding.test.ts +++ b/ironfish/src/storage/database/encoding.test.ts @@ -109,38 +109,38 @@ describe('Encoding', () => { await expect(prefixStore.get(['b', 'b'])).resolves.toBe('b') // Iteration operations - await expect(prefixStore.getAllValues()).resolves.toMatchObject(['a', 'b']) - await expect(prefixStore.getAllKeys()).resolves.toMatchObject([ + await expect(prefixStore.getAllValues()).resolves.toEqual(['a', 'b']) + await expect(prefixStore.getAllKeys()).resolves.toEqual([ ['a', 'a'], ['b', 'b'], ]) - await expect(prefixStore.getAllValues(undefined, keyRangeA)).resolves.toMatchObject(['a']) - await expect(prefixStore.getAllValues(undefined, keyRangeB)).resolves.toMatchObject(['b']) + await expect(prefixStore.getAllValues(undefined, keyRangeA)).resolves.toEqual(['a']) + await expect(prefixStore.getAllValues(undefined, keyRangeB)).resolves.toEqual(['b']) await prefixStore.clear(undefined, keyRangeA) await expect(prefixStore.get(['a', 'a'])).resolves.toBe(undefined) await expect(prefixStore.get(['b', 'b'])).resolves.toBe('b') - await expect(prefixStore.getAllValues(undefined, keyRangeA)).resolves.toMatchObject([]) - await expect(prefixStore.getAllValues(undefined, keyRangeB)).resolves.toMatchObject(['b']) + await expect(prefixStore.getAllValues(undefined, keyRangeA)).resolves.toEqual([]) + await expect(prefixStore.getAllValues(undefined, keyRangeB)).resolves.toEqual(['b']) await prefixStore.clear(undefined, keyRangeB) // Now try transactions await db.transaction(async (tx) => { await prefixStore.put(['a', 'a'], 'a', tx) - await expect(prefixStore.getAllValues(tx, keyRangeA)).resolves.toMatchObject(['a']) + await expect(prefixStore.getAllValues(tx, keyRangeA)).resolves.toEqual(['a']) await prefixStore.clear(tx, keyRangeA) - await expect(prefixStore.getAllValues(tx, keyRangeA)).resolves.toMatchObject([]) + await expect(prefixStore.getAllValues(tx, keyRangeA)).resolves.toEqual([]) await prefixStore.put(['b', 'b'], 'b', tx) - await expect(prefixStore.getAllValues(tx, keyRangeB)).resolves.toMatchObject(['b']) + await expect(prefixStore.getAllValues(tx, keyRangeB)).resolves.toEqual(['b']) await prefixStore.clear(tx, keyRangeB) - await expect(prefixStore.getAllValues(tx, keyRangeB)).resolves.toMatchObject([]) + await expect(prefixStore.getAllValues(tx, keyRangeB)).resolves.toEqual([]) }) - await expect(prefixStore.getAllValues(undefined, keyRangeA)).resolves.toMatchObject([]) - await expect(prefixStore.getAllValues(undefined, keyRangeB)).resolves.toMatchObject([]) + await expect(prefixStore.getAllValues(undefined, keyRangeA)).resolves.toEqual([]) + await expect(prefixStore.getAllValues(undefined, keyRangeB)).resolves.toEqual([]) }) it('should error with incorrect prefix size', async () => { diff --git a/ironfish/src/syncer.test.ts b/ironfish/src/syncer.test.ts index 4b0f2a82d4..9733840833 100644 --- a/ironfish/src/syncer.test.ts +++ b/ironfish/src/syncer.test.ts @@ -113,7 +113,7 @@ describe('Syncer', () => { const syncFromSpy = jest.spyOn(syncer, 'syncFrom') const [promise, , reject] = PromiseUtils.split() - syncFromSpy.mockResolvedValue(promise) + syncFromSpy.mockReturnValue(promise) syncer['startSync'](peer) expect(syncer.stopping).not.toBe(null) @@ -143,7 +143,7 @@ describe('Syncer', () => { const syncFromSpy = jest.spyOn(syncer, 'syncFrom') const [promise, resolve] = PromiseUtils.split() - syncFromSpy.mockResolvedValue(promise) + syncFromSpy.mockReturnValue(promise) syncer['startSync'](peer) expect(syncer.stopping).not.toBe(null) @@ -264,7 +264,7 @@ describe('Syncer', () => { const syncFromSpy = jest.spyOn(syncer, 'syncFrom') const [promise, resolve] = PromiseUtils.split() - syncFromSpy.mockResolvedValue(promise) + syncFromSpy.mockReturnValue(promise) syncer['startSync'](peer) // Set the nextMeasureTime to be less than now, which is the trigger to diff --git a/ironfish/src/telemetry/telemetry.test.ts b/ironfish/src/telemetry/telemetry.test.ts index 6e42e4cdac..38c19bfd5f 100644 --- a/ironfish/src/telemetry/telemetry.test.ts +++ b/ironfish/src/telemetry/telemetry.test.ts @@ -86,7 +86,9 @@ describe('Telemetry', () => { const points = telemetry['points'] expect(points).toHaveLength(currentPointsLength + 1) - expect(points[points.length - 1]).toMatchObject(mockMetric) + expect(points[points.length - 1]).toMatchObject( + mockMetric as unknown as Record, + ) }) }) diff --git a/ironfish/src/testHarness.ts b/ironfish/src/testHarness.ts new file mode 100644 index 0000000000..f89eb8062d --- /dev/null +++ b/ironfish/src/testHarness.ts @@ -0,0 +1,25 @@ +/* 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 type { Jest } from '@jest/environment' +import type { JestExpect } from '@jest/expect' +import type { Global } from '@jest/types' + +declare global { + const { + it, + test, + fit, + xit, + xtest, + describe, + xdescribe, + fdescribe, + beforeAll, + beforeEach, + afterEach, + afterAll, + }: Global.GlobalAdditions + const expect: JestExpect + const jest: Jest +} diff --git a/ironfish/src/testUtilities/matchers/blockchain.ts b/ironfish/src/testUtilities/matchers/blockchain.ts index 51df5c1209..08c770cc39 100644 --- a/ironfish/src/testUtilities/matchers/blockchain.ts +++ b/ironfish/src/testUtilities/matchers/blockchain.ts @@ -2,6 +2,7 @@ * 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 type { AsyncExpectationResult, SyncExpectationResult } from 'expect' import { diff } from 'jest-diff' import { Blockchain } from '../../blockchain' import { Block } from '../../primitives/block' @@ -12,7 +13,7 @@ import { makeError, makeResult } from './utils' function toEqualHash( self: BlockHash | null | undefined, other: BlockHash | null | undefined, -): jest.CustomMatcherResult { +): SyncExpectationResult { let error: string | null = null if (!self || !other) { @@ -26,7 +27,7 @@ function toEqualHash( return makeError(error, `Expected two serde elements to match, but they didn't`) } -function toEqualNullifier(self: Nullifier, other: Nullifier): jest.CustomMatcherResult { +function toEqualNullifier(self: Nullifier, other: Nullifier): SyncExpectationResult { let error: string | null = null if (!self || !other) { @@ -40,7 +41,7 @@ function toEqualNullifier(self: Nullifier, other: Nullifier): jest.CustomMatcher return makeError(error, `Expected two serde elements to match, but they didn't`) } -async function toAddBlock(self: Blockchain, other: Block): Promise { +async function toAddBlock(self: Blockchain, other: Block): AsyncExpectationResult { const result = await self.addBlock(other) if (!result.isAdded) { @@ -50,10 +51,7 @@ async function toAddBlock(self: Blockchain, other: Block): Promise { +async function toAddDoubleSpendBlock(self: Blockchain, other: Block): AsyncExpectationResult { // Mock data stores to allow creation of a double spend chain const transactionHashMock = jest .spyOn(self, 'transactionHashHasBlock') @@ -83,13 +81,12 @@ expect.extend({ toAddDoubleSpendBlock: toAddDoubleSpendBlock, }) -declare global { - namespace jest { - interface Matchers { - toEqualNullifier(other: Nullifier): R - toEqualHash(other: BlockHash | null | undefined): R - toAddBlock(block: Block): Promise - toAddDoubleSpendBlock(block: Block): Promise - } +declare module 'expect' { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Matchers, T = unknown> { + toEqualNullifier(other: Nullifier): R + toEqualHash(other: BlockHash | null | undefined): R + toAddBlock(block: Block): Promise + toAddDoubleSpendBlock(block: Block): Promise } } diff --git a/ironfish/src/testUtilities/matchers/buffer.ts b/ironfish/src/testUtilities/matchers/buffer.ts index 7e9caa4664..f5f20adee8 100644 --- a/ironfish/src/testUtilities/matchers/buffer.ts +++ b/ironfish/src/testUtilities/matchers/buffer.ts @@ -2,13 +2,14 @@ * 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 type { SyncExpectationResult } from 'expect' import { diff } from 'jest-diff' import { makeResult } from './utils' function toEqualBuffer( self: Buffer | null | undefined, other: Buffer | null | undefined, -): jest.CustomMatcherResult { +): SyncExpectationResult { const pass = self === other || (!self && !other) || (self && other && self.equals(other)) if (!pass) { @@ -28,10 +29,9 @@ function toEqualBuffer( expect.extend({ toEqualBuffer: toEqualBuffer }) -declare global { - namespace jest { - interface Matchers { - toEqualBuffer(other: Buffer | null | undefined): R - } +declare module 'expect' { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Matchers, T = unknown> { + toEqualBuffer(other: Buffer | null | undefined): R } } diff --git a/ironfish/src/testUtilities/matchers/merkletree.ts b/ironfish/src/testUtilities/matchers/merkletree.ts index 373f141928..674856b4e3 100644 --- a/ironfish/src/testUtilities/matchers/merkletree.ts +++ b/ironfish/src/testUtilities/matchers/merkletree.ts @@ -2,19 +2,19 @@ * 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 type { AsyncExpectationResult, SyncExpectationResult } from 'expect' import { diff } from 'jest-diff' import { MerkleTree, Witness, WitnessSide } from '../../merkletree' import { NodeValue } from '../../merkletree/schema' import { makeError } from './utils' -declare global { - namespace jest { - interface Matchers { - toHaveLeaves(characters: string, parents: number[]): Promise - toHaveNodes(nodeSpecs: [number, WitnessSide, number, string][]): Promise - toMatchTree(other: MerkleTree): Promise - toMatchWitness(treeSize: number, rootHash: string, authPath: [WitnessSide, string][]): R - } +declare module 'expect' { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Matchers, T = unknown> { + toHaveLeaves(characters: string, parents: number[]): Promise + toHaveNodes(nodeSpecs: [number, WitnessSide, number, string][]): Promise + toMatchTree(other: MerkleTree): Promise + toMatchWitness(treeSize: number, rootHash: string, authPath: [WitnessSide, string][]): R } } @@ -23,7 +23,7 @@ expect.extend({ tree: MerkleTree, characters: string, parents: number[], - ): Promise { + ): AsyncExpectationResult { let error: string | null = null const treeSize = await tree.size() @@ -54,7 +54,7 @@ expect.extend({ async toHaveNodes( tree: MerkleTree, nodeSpecs: [number, WitnessSide, number, string][], - ): Promise { + ): AsyncExpectationResult { let error: string | null = null const treeNodes = await tree.nodes.getAllValues() @@ -105,7 +105,7 @@ expect.extend({ async toMatchTree( tree: MerkleTree, other: MerkleTree, - ): Promise { + ): AsyncExpectationResult { let error: string | null = null const treeLeafCount = await tree.getCount('Leaves') const treeNodeCount = await tree.getCount('Nodes') @@ -152,7 +152,7 @@ expect.extend({ treeSize: number, rootHash: string, authenticationPath: [WitnessSide, string][], - ): jest.CustomMatcherResult { + ): SyncExpectationResult { let error: string | null = null if (witness === undefined) { diff --git a/ironfish/src/testUtilities/matchers/string.ts b/ironfish/src/testUtilities/matchers/string.ts index 67ad51654e..06954ba470 100644 --- a/ironfish/src/testUtilities/matchers/string.ts +++ b/ironfish/src/testUtilities/matchers/string.ts @@ -2,9 +2,10 @@ * 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 type { SyncExpectationResult } from 'expect' import { makeResult } from './utils' -function toBeBase64(self: string | null | undefined): jest.CustomMatcherResult { +function toBeBase64(self: string | null | undefined): SyncExpectationResult { const pass = !!self && self === Buffer.from(self, 'base64').toString('base64') if (!pass) { @@ -16,10 +17,9 @@ function toBeBase64(self: string | null | undefined): jest.CustomMatcherResult { expect.extend({ toBeBase64 }) -declare global { - namespace jest { - interface Matchers { - toBeBase64(): R - } +declare module 'expect' { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Matchers, T = unknown> { + toBeBase64(): R } } diff --git a/ironfish/src/testUtilities/mocks.ts b/ironfish/src/testUtilities/mocks.ts index a39ea31c6d..9f37deb686 100644 --- a/ironfish/src/testUtilities/mocks.ts +++ b/ironfish/src/testUtilities/mocks.ts @@ -30,7 +30,7 @@ export function mockWallet(): any { export function mockVerifier(): any { return { - verifyNewTransaction: jest.fn().mockResolvedValue({}), + verifyNewTransaction: jest.fn<(...args: any[]) => Promise>().mockResolvedValue({}), } } @@ -107,6 +107,6 @@ export function mockWorkerPool(): any { export function mockConfig(values: Record): any { return { - get: jest.fn((x) => values[x]), + get: jest.fn((x: string) => values[x]), } } diff --git a/ironfish/src/testUtilities/utils.test.ts b/ironfish/src/testUtilities/utils.test.ts deleted file mode 100644 index 75438d67b5..0000000000 --- a/ironfish/src/testUtilities/utils.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* 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 { mockImplementationShuffle } from './utils' - -describe('Mocks', () => { - it('should shuffle mock', async () => { - const mock = jest.fn() - - const results: number[] = [] - - mockImplementationShuffle<[number], void>(mock, (value: number) => { - results.push(value) - return Promise.resolve() - }) - - const promises = [] - for (let i = 0; i < 10; ++i) { - promises.push(mock(i)) - } - await Promise.all(promises) - - expect(results).toHaveLength(10) - }) - - it('should allow cancelation', () => { - jest.useFakeTimers() - - const mock = jest.fn() - const results: number[] = [] - - function mockImplementation(value: number) { - results.push(value) - return Promise.resolve(value) - } - - // it should have the result from the shuffled result - mockImplementationShuffle(mock, mockImplementation, 1) - mock(0) - jest.runAllTimers() - expect(results).toHaveLength(1) - - results.length = 0 - - // when we call cancel it should not have the result - const cancelShuffle = mockImplementationShuffle(mock, mockImplementation, 1) - mock(0) - cancelShuffle() - jest.runAllTimers() - expect(results).toHaveLength(0) - }) -}) diff --git a/ironfish/src/testUtilities/utils.ts b/ironfish/src/testUtilities/utils.ts index dc8f961bff..9deb2e1c84 100644 --- a/ironfish/src/testUtilities/utils.ts +++ b/ironfish/src/testUtilities/utils.ts @@ -66,67 +66,3 @@ export async function splitNotes( Assert.isNotNull(account.spendingKey) return transaction.post(account.spendingKey) } - -/** - * Asserts the type of a given function as a Jest mock. - */ -export function typeMock( - func: (...args: [...T]) => R, -): jest.Mock { - return func as jest.Mock -} - -/** - * Used to shuffle the responses from an asynchronous API call using a debounce strategy. - * @param mock The mock to intercept calls for and shuffle - * @param mocked The mock function to replace mock with - * @param time The maximum amount of debounce time to allow before returning shuffled results - */ -export function mockImplementationShuffle( - mock: jest.Mock, TArgs>, - mocked: (...args: TArgs) => Promise, - time = 10, -): () => void { - type PromiseResolve = (result: Promise) => void - const buffer: [TArgs, PromiseResolve][] = [] - let lastTimeout: number | null = null - let lastSend: number | null = null - - mock.mockImplementation((...args: TArgs): Promise => { - const promise = new Promise>((resolve) => { - if (lastTimeout) { - clearTimeout(lastTimeout) - } - - buffer.push([args, resolve]) - - function send() { - lastSend = Date.now() - - const shuffled = buffer.slice().sort(() => Math.random() - 0.5) - buffer.length = 0 - - for (const [args, resolve] of shuffled) { - resolve(mocked(...args)) - } - } - - // Force a send if the maximum amount of time has elapsed - if (lastSend !== null && Date.now() - lastSend > time) { - send() - return - } - - // Start the debounce timer - lastTimeout = setTimeout(send, time) as unknown as number - }) - - return promise.then((r) => r) - }) - - return () => { - if (lastTimeout) { - clearTimeout(lastTimeout) - } - } -} diff --git a/ironfish/src/utils/retry.test.ts b/ironfish/src/utils/retry.test.ts index 6d36ca0e91..f68388e81a 100644 --- a/ironfish/src/utils/retry.test.ts +++ b/ironfish/src/utils/retry.test.ts @@ -10,7 +10,7 @@ describe('Retry', () => { jest.useFakeTimers({ legacyFakeTimers: false }) it('immediately returns when there is no error', async () => { - const fn = jest.fn().mockResolvedValue(123) + const fn = jest.fn<() => Promise>().mockResolvedValue(123) const retry = new Retry({ delay: 1000, jitter: 0.2, @@ -23,7 +23,7 @@ describe('Retry', () => { describe('without maxRetries', () => { it('keeps retrying until the function succeeds', async () => { - const fn = jest.fn() + const fn = jest.fn<() => Promise>() const retry = new Retry({ delay: 1000, jitter: 0.2, @@ -53,7 +53,7 @@ describe('Retry', () => { describe('with maxRetries', () => { it('keeps retrying until the maximum number of retries is reached', async () => { const maxRetries = 0 //10 - const fn = jest.fn() + const fn = jest.fn<() => Promise>() const retry = new Retry({ delay: 1000, jitter: 0.2, diff --git a/ironfish/src/wallet/scanner/noteDecryptor.test.ts b/ironfish/src/wallet/scanner/noteDecryptor.test.ts index d40b2c9775..c3490595cb 100644 --- a/ironfish/src/wallet/scanner/noteDecryptor.test.ts +++ b/ironfish/src/wallet/scanner/noteDecryptor.test.ts @@ -50,10 +50,7 @@ describe('BackgroundNoteDecryptor', () => { decryptor.start() - const callback = jest.fn< - ReturnType, - jest.ArgumentsOf - >() + const callback = jest.fn() for (const block of blocks) { await decryptor.decryptNotesFromBlock( diff --git a/ironfish/src/wallet/scanner/remoteChainProcessor.test.ts b/ironfish/src/wallet/scanner/remoteChainProcessor.test.ts index 78a385b128..dae7a3f897 100644 --- a/ironfish/src/wallet/scanner/remoteChainProcessor.test.ts +++ b/ironfish/src/wallet/scanner/remoteChainProcessor.test.ts @@ -2,6 +2,7 @@ * 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 type { Mock } from 'jest-mock' import { BlockHeader } from '../../primitives' import { ALL_API_NAMESPACES, RpcMemoryClient } from '../../rpc' import { createNodeTest, useMinerBlockFixture } from '../../testUtilities' @@ -46,7 +47,7 @@ describe('RemoteChainProcessor', () => { maxQueueSize: 10, }) - const onEvent: jest.Mock = jest.fn() + const onEvent: Mock<(header: BlockHeader, event: 'add' | 'remove') => void> = jest.fn() processor.onAdd.on((block) => onEvent(block.header, 'add')) processor.onRemove.on((block) => onEvent(block.header, 'remove')) @@ -101,7 +102,7 @@ describe('RemoteChainProcessor', () => { maxQueueSize: 10, }) - const onEvent: jest.Mock = jest.fn() + const onEvent: Mock<(header: BlockHeader, event: 'add' | 'remove') => void> = jest.fn() processor.onAdd.on((block) => onEvent(block.header, 'add')) processor.onRemove.on((block) => onEvent(block.header, 'remove')) diff --git a/ironfish/src/wallet/wallet.test.slow.ts b/ironfish/src/wallet/wallet.test.slow.ts index 9aa371f52e..754c7ac025 100644 --- a/ironfish/src/wallet/wallet.test.slow.ts +++ b/ironfish/src/wallet/wallet.test.slow.ts @@ -1,6 +1,7 @@ /* 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 type { SpiedFunction } from 'jest-mock' import { Asset, ASSET_ID_LENGTH, generateKey, multisig } from '@ironfish/rust-nodejs' import { Assert } from '../assert' import { Transaction } from '../primitives' @@ -20,8 +21,8 @@ import { AssertMultisigSigner } from '../wallet' describe('Wallet', () => { const nodeTest = createNodeTest() - let targetMeetsSpy: jest.SpyInstance - let targetSpy: jest.SpyInstance + let targetMeetsSpy: SpiedFunction + let targetSpy: SpiedFunction beforeAll(async () => { targetMeetsSpy = jest.spyOn(Target, 'meets').mockImplementation(() => true) diff --git a/ironfish/src/wallet/wallet.test.ts b/ironfish/src/wallet/wallet.test.ts index 79202b7135..71e3357ad8 100644 --- a/ironfish/src/wallet/wallet.test.ts +++ b/ironfish/src/wallet/wallet.test.ts @@ -2059,7 +2059,7 @@ describe('Wallet', () => { ).toEqual(AssetStatus.UNCONFIRMED) // Remove the head and check status - jest.spyOn(account, 'getHead').mockResolvedValueOnce(Promise.resolve(null)) + jest.spyOn(account, 'getHead').mockResolvedValueOnce(null) expect(await node.wallet.getAssetStatus(account, assetValue)).toEqual(AssetStatus.UNKNOWN) }) }) diff --git a/ironfish/src/workerPool/tasks/submitTelemetry.test.ts b/ironfish/src/workerPool/tasks/submitTelemetry.test.ts index a45bcf36b1..9d35b67ca7 100644 --- a/ironfish/src/workerPool/tasks/submitTelemetry.test.ts +++ b/ironfish/src/workerPool/tasks/submitTelemetry.test.ts @@ -65,9 +65,7 @@ describe('SubmitTelemetryResponse', () => { describe('SubmitTelemetryTask', () => { describe('execute', () => { it('submits points to the API', async () => { - const submitTelemetryPointsToApi = jest - .spyOn(axios, 'post') - .mockImplementationOnce(jest.fn()) + const submitTelemetryPointsToApi = jest.spyOn(axios, 'post').mockResolvedValueOnce(null) const mockMetric: Metric = { measurement: 'node', fields: [ diff --git a/package.json b/package.json index a19c52affb..32b5f045d1 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "coverage:upload": "lerna exec '\"yarn codecov -t $CODECOV_TOKEN -f ./coverage/clover.xml -F $LERNA_PACKAGE_NAME -p $ROOT_PATH/ --disable=gcov\"'" }, "devDependencies": { - "@types/jest": "29.5.8", "@typescript-eslint/eslint-plugin": "6.19.0", "@typescript-eslint/parser": "6.19.0", "codecov": "3.8.3", diff --git a/yarn.lock b/yarn.lock index 38709b4636..b94a91a5a6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -511,13 +511,6 @@ "@types/node" "*" jest-mock "^29.7.0" -"@jest/expect-utils@^29.3.1": - version "29.3.1" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.3.1.tgz#531f737039e9b9e27c42449798acb5bba01935b6" - integrity sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g== - dependencies: - jest-get-type "^29.2.0" - "@jest/expect-utils@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6" @@ -2772,14 +2765,6 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@29.5.8": - version "29.5.8" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.8.tgz#ed5c256fe2bc7c38b1915ee5ef1ff24a3427e120" - integrity sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - "@types/json-schema@^7.0.12": version "7.0.13" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85" @@ -4743,11 +4728,6 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -diff-sequences@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e" - integrity sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ== - diff-sequences@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" @@ -5233,17 +5213,6 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== -expect@^29.0.0: - version "29.3.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.3.1.tgz#92877aad3f7deefc2e3f6430dd195b92295554a6" - integrity sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA== - dependencies: - "@jest/expect-utils" "^29.3.1" - jest-get-type "^29.2.0" - jest-matcher-utils "^29.3.1" - jest-message-util "^29.3.1" - jest-util "^29.3.1" - expect@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc" @@ -6719,16 +6688,6 @@ jest-config@^29.7.0: slash "^3.0.0" strip-json-comments "^3.1.1" -jest-diff@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.3.1.tgz#d8215b72fed8f1e647aed2cae6c752a89e757527" - integrity sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.3.1" - jest-get-type "^29.2.0" - pretty-format "^29.3.1" - jest-diff@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" @@ -6769,11 +6728,6 @@ jest-environment-node@^29.7.0: jest-mock "^29.7.0" jest-util "^29.7.0" -jest-get-type@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" - integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== - jest-get-type@^29.6.3: version "29.6.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" @@ -6848,16 +6802,6 @@ jest-leak-detector@^29.7.0: jest-get-type "^29.6.3" pretty-format "^29.7.0" -jest-matcher-utils@^29.3.1: - version "29.3.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz#6e7f53512f80e817dfa148672bd2d5d04914a572" - integrity sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ== - dependencies: - chalk "^4.0.0" - jest-diff "^29.3.1" - jest-get-type "^29.2.0" - pretty-format "^29.3.1" - jest-matcher-utils@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12" @@ -8962,7 +8906,7 @@ pretty-bytes@^5.3.0: resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== -pretty-format@^29.0.0, pretty-format@^29.3.1: +pretty-format@^29.3.1: version "29.3.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.3.1.tgz#1841cac822b02b4da8971dacb03e8a871b4722da" integrity sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg== From 15b8bb00aa054da3451b2cf66b9415e1224edbd7 Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Thu, 11 Jul 2024 15:19:10 -0700 Subject: [PATCH 41/81] Replace ux.prompt for oclif 4.x upgrade prep (#5121) ux.prompt is being removed in oclif 4.x, so in preparation for that, we will use inquirer for input prompts. We are already using inquirer for selection and confirm prompts, so there isn't really anything new in this change. --- ironfish-cli/src/commands/faucet.ts | 23 ++++++++----------- .../src/commands/wallet/chainport/send.ts | 6 ++--- ironfish-cli/src/commands/wallet/create.ts | 7 +++--- ironfish-cli/src/commands/wallet/delete.ts | 3 ++- ironfish-cli/src/commands/wallet/import.ts | 9 +++----- ironfish-cli/src/commands/wallet/mint.ts | 11 +++------ .../commands/wallet/multisig/dealer/create.ts | 7 +++--- .../commands/wallet/multisig/dkg/round1.ts | 7 +++--- .../commands/wallet/multisig/dkg/round2.ts | 7 +++--- .../commands/wallet/multisig/dkg/round3.ts | 9 ++++---- .../wallet/multisig/participant/create.ts | 11 ++++----- .../src/commands/wallet/notes/combine.ts | 11 ++++----- ironfish-cli/src/commands/wallet/send.ts | 11 ++++----- ironfish-cli/src/ui/index.ts | 2 +- ironfish-cli/src/ui/{confirm.ts => prompt.ts} | 23 +++++++++++++++++++ ironfish-cli/src/utils/currency.ts | 6 ++--- ironfish-cli/src/utils/expiration.ts | 4 ++-- 17 files changed, 77 insertions(+), 80 deletions(-) rename ironfish-cli/src/ui/{confirm.ts => prompt.ts} (63%) diff --git a/ironfish-cli/src/commands/faucet.ts b/ironfish-cli/src/commands/faucet.ts index 0653eba999..d4fde5c27d 100644 --- a/ironfish-cli/src/commands/faucet.ts +++ b/ironfish-cli/src/commands/faucet.ts @@ -7,6 +7,7 @@ import { Flags, ux } from '@oclif/core' import { IronfishCommand } from '../command' import { RemoteFlags } from '../flags' import { ONE_FISH_IMAGE, TWO_FISH_IMAGE } from '../images' +import { inputPrompt } from '../ui' const FAUCET_DISABLED = false @@ -47,9 +48,7 @@ export class FaucetCommand extends IronfishCommand { if (!email) { email = - (await ux.prompt('Enter your email to stay updated with Iron Fish', { - required: false, - })) || undefined + (await inputPrompt('Enter your email to stay updated with Iron Fish')) || undefined } // Create an account if one is not set @@ -59,9 +58,7 @@ export class FaucetCommand extends IronfishCommand { if (!accountName) { this.log(`You don't have a default account set up yet. Let's create one first!`) accountName = - (await ux.prompt('Please enter the name of your new Iron Fish account', { - required: false, - })) || 'default' + (await inputPrompt('Please enter the name of your new Iron Fish account')) || 'default' await client.wallet.createAccount({ name: accountName, default: true }) } @@ -89,17 +86,17 @@ export class FaucetCommand extends IronfishCommand { this.log( ` - ${TWO_FISH_IMAGE} + ${TWO_FISH_IMAGE} -Congratulations! The Iron Fish Faucet just added your request to the queue! + Congratulations! The Iron Fish Faucet just added your request to the queue! -It will be processed within the next hour and $IRON will be sent directly to your account. + It will be processed within the next hour and $IRON will be sent directly to your account. -Check your balance by running: - - ironfish wallet:balance + Check your balance by running: + - ironfish wallet:balance -Learn how to send a transaction by running: - - ironfish wallet:send --help`, + Learn how to send a transaction by running: + - ironfish wallet:send --help`, ) } } diff --git a/ironfish-cli/src/commands/wallet/chainport/send.ts b/ironfish-cli/src/commands/wallet/chainport/send.ts index ffdfd61265..324e9f5c82 100644 --- a/ironfish-cli/src/commands/wallet/chainport/send.ts +++ b/ironfish-cli/src/commands/wallet/chainport/send.ts @@ -17,7 +17,7 @@ import { Flags, ux } from '@oclif/core' import inquirer from 'inquirer' import { IronfishCommand } from '../../../command' import { HexFlag, IronFlag, RemoteFlags, ValueFlag } from '../../../flags' -import { confirmOrQuit } from '../../../ui' +import { confirmOrQuit, inputPrompt } from '../../../ui' import { selectAsset } from '../../../utils' import { ChainportBridgeTransaction, @@ -173,9 +173,7 @@ export class BridgeCommand extends IronfishCommand { } if (!to) { - to = await ux.prompt('Enter the public address of the recipient', { - required: true, - }) + to = await inputPrompt('Enter the public address of the recipient', true) } if (!isEthereumAddress(to)) { diff --git a/ironfish-cli/src/commands/wallet/create.ts b/ironfish-cli/src/commands/wallet/create.ts index 01ebb8e5ed..dc17c19de3 100644 --- a/ironfish-cli/src/commands/wallet/create.ts +++ b/ironfish-cli/src/commands/wallet/create.ts @@ -2,9 +2,10 @@ * 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 { Args, ux } from '@oclif/core' +import { Args } from '@oclif/core' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' +import { inputPrompt } from '../../ui' export class CreateCommand extends IronfishCommand { static description = `Create a new account for sending and receiving coins` @@ -25,9 +26,7 @@ export class CreateCommand extends IronfishCommand { let name = args.account if (!name) { - name = await ux.prompt('Enter the name of the account', { - required: true, - }) + name = await inputPrompt('Enter the name of the account', true) } const client = await this.sdk.connectRpc() diff --git a/ironfish-cli/src/commands/wallet/delete.ts b/ironfish-cli/src/commands/wallet/delete.ts index 4f55289658..1248541ab5 100644 --- a/ironfish-cli/src/commands/wallet/delete.ts +++ b/ironfish-cli/src/commands/wallet/delete.ts @@ -5,6 +5,7 @@ import { Args, Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' +import { inputPrompt } from '../../ui' export class DeleteCommand extends IronfishCommand { static description = `Permanently delete an account` @@ -39,7 +40,7 @@ export class DeleteCommand extends IronfishCommand { ux.action.stop() if (response.content.needsConfirm) { - const value = await ux.prompt(`Are you sure? Type ${account} to confirm`) + const value = await inputPrompt(`Are you sure? Type ${account} to confirm`) if (value !== account) { this.log(`Aborting: ${value} did not match ${account}`) diff --git a/ironfish-cli/src/commands/wallet/import.ts b/ironfish-cli/src/commands/wallet/import.ts index 81cdbb907e..9c0b1ffa1b 100644 --- a/ironfish-cli/src/commands/wallet/import.ts +++ b/ironfish-cli/src/commands/wallet/import.ts @@ -10,6 +10,7 @@ import { import { Args, Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' +import { inputPrompt } from '../../ui' import { importFile, importPipe, longPrompt } from '../../utils/input' import { Ledger } from '../../utils/ledger' @@ -92,9 +93,7 @@ export class ImportCommand extends IronfishCommand { this.log() this.log(`Found existing account with name '${flags.name}'`) - const name = await ux.prompt('Enter a different name for the account', { - required: true, - }) + const name = await inputPrompt('Enter a different name for the account', true) if (name === flags.name) { this.error(`Entered the same name: '${name}'`) } @@ -125,9 +124,7 @@ export class ImportCommand extends IronfishCommand { this.log(e.codeMessage) } - const name = await ux.prompt(message, { - required: true, - }) + const name = await inputPrompt(message, true) if (name === flags.name) { this.error(`Entered the same name: '${name}'`) } diff --git a/ironfish-cli/src/commands/wallet/mint.ts b/ironfish-cli/src/commands/wallet/mint.ts index 2b5025c236..93df359872 100644 --- a/ironfish-cli/src/commands/wallet/mint.ts +++ b/ironfish-cli/src/commands/wallet/mint.ts @@ -17,7 +17,7 @@ import { import { Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../command' import { IronFlag, RemoteFlags, ValueFlag } from '../../flags' -import { confirmOrQuit, confirmPrompt } from '../../ui' +import { confirmOrQuit, confirmPrompt, inputPrompt } from '../../ui' import { selectAsset } from '../../utils/asset' import { promptCurrency } from '../../utils/currency' import { promptExpiration } from '../../utils/expiration' @@ -158,16 +158,11 @@ export class Mint extends IronfishCommand { if (isMintingNewAsset) { if (!name) { - name = await ux.prompt('Enter the name for the new asset', { - required: true, - }) + name = await inputPrompt('Enter the name for the new asset', true) } if (!metadata) { - metadata = await ux.prompt('Enter metadata for the new asset', { - default: '', - required: false, - }) + metadata = await inputPrompt('Enter metadata for the new asset') } const newAsset = new Asset(accountPublicKey, name, metadata) diff --git a/ironfish-cli/src/commands/wallet/multisig/dealer/create.ts b/ironfish-cli/src/commands/wallet/multisig/dealer/create.ts index ed4135d20f..f0283cdd90 100644 --- a/ironfish-cli/src/commands/wallet/multisig/dealer/create.ts +++ b/ironfish-cli/src/commands/wallet/multisig/dealer/create.ts @@ -6,6 +6,7 @@ import { AccountImport } from '@ironfish/sdk/src/wallet/exporter' import { Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../../../command' import { RemoteFlags } from '../../../../flags' +import { inputPrompt } from '../../../../ui' import { longPrompt } from '../../../../utils/input' export class MultisigCreateDealer extends IronfishCommand { @@ -54,9 +55,7 @@ export class MultisigCreateDealer extends IronfishCommand { let minSigners = flags.minSigners if (!minSigners) { - const input = await ux.prompt('Enter the number of minimum signers', { - required: true, - }) + const input = await inputPrompt('Enter the number of minimum signers', true) minSigners = parseInt(input) if (isNaN(minSigners) || minSigners < 2) { this.error('Minimum number of signers must be at least 2') @@ -131,7 +130,7 @@ export class MultisigCreateDealer extends IronfishCommand { let name = inputName do { - name = name ?? (await ux.prompt('Enter a name for the coordinator', { required: true })) + name = name ?? (await inputPrompt('Enter a name for the coordinator', true)) if (accountNames.has(name)) { this.log(`Account with name ${name} already exists`) diff --git a/ironfish-cli/src/commands/wallet/multisig/dkg/round1.ts b/ironfish-cli/src/commands/wallet/multisig/dkg/round1.ts index fd92ba98b9..08a2c03eca 100644 --- a/ironfish-cli/src/commands/wallet/multisig/dkg/round1.ts +++ b/ironfish-cli/src/commands/wallet/multisig/dkg/round1.ts @@ -1,9 +1,10 @@ /* 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 { Flags, ux } from '@oclif/core' +import { Flags } from '@oclif/core' import { IronfishCommand } from '../../../../command' import { RemoteFlags } from '../../../../flags' +import { inputPrompt } from '../../../../ui' import { longPrompt } from '../../../../utils/input' import { selectSecret } from '../../../../utils/multisig' @@ -57,9 +58,7 @@ export class DkgRound1Command extends IronfishCommand { let minSigners = flags.minSigners if (!minSigners) { - const input = await ux.prompt('Enter the number of minimum signers', { - required: true, - }) + const input = await inputPrompt('Enter the number of minimum signers', true) minSigners = parseInt(input) if (isNaN(minSigners) || minSigners < 2) { this.error('Minimum number of signers must be at least 2') diff --git a/ironfish-cli/src/commands/wallet/multisig/dkg/round2.ts b/ironfish-cli/src/commands/wallet/multisig/dkg/round2.ts index 318003df48..8e8c1fe3f7 100644 --- a/ironfish-cli/src/commands/wallet/multisig/dkg/round2.ts +++ b/ironfish-cli/src/commands/wallet/multisig/dkg/round2.ts @@ -1,9 +1,10 @@ /* 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 { Flags, ux } from '@oclif/core' +import { Flags } from '@oclif/core' import { IronfishCommand } from '../../../../command' import { RemoteFlags } from '../../../../flags' +import { inputPrompt } from '../../../../ui' import { longPrompt } from '../../../../utils/input' import { selectSecret } from '../../../../utils/multisig' @@ -41,9 +42,9 @@ export class DkgRound2Command extends IronfishCommand { let round1SecretPackage = flags.round1SecretPackage if (!round1SecretPackage) { - round1SecretPackage = await ux.prompt( + round1SecretPackage = await inputPrompt( `Enter the round 1 secret package for participant ${participantName}`, - { required: true }, + true, ) } diff --git a/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts b/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts index 4a667bb46a..999cb280c2 100644 --- a/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts +++ b/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts @@ -1,9 +1,10 @@ /* 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 { Flags, ux } from '@oclif/core' +import { Flags } from '@oclif/core' import { IronfishCommand } from '../../../../command' import { RemoteFlags } from '../../../../flags' +import { inputPrompt } from '../../../../ui' import { longPrompt } from '../../../../utils/input' import { selectSecret } from '../../../../utils/multisig' @@ -51,11 +52,9 @@ export class DkgRound3Command extends IronfishCommand { let round2SecretPackage = flags.round2SecretPackage if (!round2SecretPackage) { - round2SecretPackage = await ux.prompt( + round2SecretPackage = await inputPrompt( `Enter the round 2 encrypted secret package for participant ${participantName}`, - { - required: true, - }, + true, ) } diff --git a/ironfish-cli/src/commands/wallet/multisig/participant/create.ts b/ironfish-cli/src/commands/wallet/multisig/participant/create.ts index f07828b885..f0499a670a 100644 --- a/ironfish-cli/src/commands/wallet/multisig/participant/create.ts +++ b/ironfish-cli/src/commands/wallet/multisig/participant/create.ts @@ -2,9 +2,10 @@ * 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 { RPC_ERROR_CODES, RpcRequestError } from '@ironfish/sdk' -import { Flags, ux } from '@oclif/core' +import { Flags } from '@oclif/core' import { IronfishCommand } from '../../../../command' import { RemoteFlags } from '../../../../flags' +import { inputPrompt } from '../../../../ui' export class MultisigIdentityCreate extends IronfishCommand { static description = `Create a multisig participant identity` @@ -21,9 +22,7 @@ export class MultisigIdentityCreate extends IronfishCommand { const { flags } = await this.parse(MultisigIdentityCreate) let name = flags.name if (!name) { - name = await ux.prompt('Enter a name for the identity', { - required: true, - }) + name = await inputPrompt('Enter a name for the identity', true) } const client = await this.sdk.connectRpc() @@ -38,9 +37,7 @@ export class MultisigIdentityCreate extends IronfishCommand { ) { this.log() this.log(e.codeMessage) - name = await ux.prompt('Enter a new name for the identity', { - required: true, - }) + name = await inputPrompt('Enter a new name for the identity', true) } else { throw e } diff --git a/ironfish-cli/src/commands/wallet/notes/combine.ts b/ironfish-cli/src/commands/wallet/notes/combine.ts index fbc0a75068..f6c34097d0 100644 --- a/ironfish-cli/src/commands/wallet/notes/combine.ts +++ b/ironfish-cli/src/commands/wallet/notes/combine.ts @@ -12,11 +12,11 @@ import { TimeUtils, Transaction, } from '@ironfish/sdk' -import { Flags, ux } from '@oclif/core' +import { Flags } from '@oclif/core' import inquirer from 'inquirer' import { IronfishCommand } from '../../../command' import { HexFlag, IronFlag, RemoteFlags } from '../../../flags' -import { confirmOrQuit, table } from '../../../ui' +import { confirmOrQuit, inputPrompt, table } from '../../../ui' import { getAssetsByIDs, selectAsset } from '../../../utils' import { getExplorer } from '../../../utils/explorer' import { selectFee } from '../../../utils/fees' @@ -132,9 +132,7 @@ export class CombineNotesCommand extends IronfishCommand { // eslint-disable-next-line no-constant-condition while (true) { - const result = await ux.prompt('Enter the number of notes', { - required: true, - }) + const result = await inputPrompt('Enter the number of notes', true) const notesToCombine = parseInt(result) @@ -288,8 +286,7 @@ export class CombineNotesCommand extends IronfishCommand { const totalAmount = notes.reduce((acc, note) => acc + BigInt(note.value), 0n) - const memo = - flags.memo ?? (await ux.prompt('Enter the memo (or leave blank)', { required: false })) + const memo = flags.memo ?? (await inputPrompt('Enter the memo (or leave blank)')) const expiration = await this.calculateExpiration(client, spendPostTime, numberOfNotes) diff --git a/ironfish-cli/src/commands/wallet/send.ts b/ironfish-cli/src/commands/wallet/send.ts index 96c88d8f50..5e1996f5dd 100644 --- a/ironfish-cli/src/commands/wallet/send.ts +++ b/ironfish-cli/src/commands/wallet/send.ts @@ -12,10 +12,10 @@ import { TimeUtils, Transaction, } from '@ironfish/sdk' -import { Flags, ux } from '@oclif/core' +import { Flags } from '@oclif/core' import { IronfishCommand } from '../../command' import { HexFlag, IronFlag, RemoteFlags, ValueFlag } from '../../flags' -import { confirmOrQuit } from '../../ui' +import { confirmOrQuit, inputPrompt } from '../../ui' import { selectAsset } from '../../utils/asset' import { promptCurrency } from '../../utils/currency' import { promptExpiration } from '../../utils/expiration' @@ -205,13 +205,10 @@ export class Send extends IronfishCommand { } if (!to) { - to = await ux.prompt('Enter the public address of the recipient', { - required: true, - }) + to = await inputPrompt('Enter the public address of the recipient', true) } - const memo = - flags.memo ?? (await ux.prompt('Enter the memo (or leave blank)', { required: false })) + const memo = flags.memo ?? (await inputPrompt('Enter the memo (or leave blank)')) if (!isValidPublicAddress(to)) { this.log(`A valid public address is required`) diff --git a/ironfish-cli/src/ui/index.ts b/ironfish-cli/src/ui/index.ts index af9f47d608..75326b65a6 100644 --- a/ironfish-cli/src/ui/index.ts +++ b/ironfish-cli/src/ui/index.ts @@ -2,6 +2,6 @@ * 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/. */ -export * from './confirm' +export * from './prompt' export * from './progressBar' export * from './table' diff --git a/ironfish-cli/src/ui/confirm.ts b/ironfish-cli/src/ui/prompt.ts similarity index 63% rename from ironfish-cli/src/ui/confirm.ts rename to ironfish-cli/src/ui/prompt.ts index 121f60d01f..dc272c546c 100644 --- a/ironfish-cli/src/ui/confirm.ts +++ b/ironfish-cli/src/ui/prompt.ts @@ -5,6 +5,29 @@ import { ux } from '@oclif/core' import inquirer from 'inquirer' +async function _inputPrompt(message: string): Promise { + const result: { prompt: string } = await inquirer.prompt({ + type: 'input', + name: 'prompt', + message: `${message}:`, + }) + return result.prompt.trim() +} + +export async function inputPrompt(message: string, required: boolean = false): Promise { + let userInput: string = '' + + if (required) { + while (!userInput) { + userInput = await _inputPrompt(message) + } + } else { + userInput = await _inputPrompt(message) + } + + return userInput +} + export async function confirmPrompt(message: string): Promise { const result: { prompt: boolean } = await inquirer.prompt({ type: 'confirm', diff --git a/ironfish-cli/src/utils/currency.ts b/ironfish-cli/src/utils/currency.ts index da1720128f..67740de52a 100644 --- a/ironfish-cli/src/utils/currency.ts +++ b/ironfish-cli/src/utils/currency.ts @@ -4,7 +4,7 @@ import { Asset } from '@ironfish/rust-nodejs' import { Assert, CurrencyUtils, Logger, RpcAssetVerification, RpcClient } from '@ironfish/sdk' -import { ux } from '@oclif/core' +import { inputPrompt } from '../ui' /** * This prompts the user to enter an amount of currency in the major @@ -57,9 +57,7 @@ export async function promptCurrency(options: { // eslint-disable-next-line no-constant-condition while (true) { - const input = await ux.prompt(text, { - required: options.required, - }) + const input = await inputPrompt(text, options.required) if (!input) { return null diff --git a/ironfish-cli/src/utils/expiration.ts b/ironfish-cli/src/utils/expiration.ts index 4ce9720297..1755d58c4f 100644 --- a/ironfish-cli/src/utils/expiration.ts +++ b/ironfish-cli/src/utils/expiration.ts @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { Logger, RpcClient } from '@ironfish/sdk' -import { ux } from '@oclif/core' +import { inputPrompt } from '../ui' export async function promptExpiration(options: { client: RpcClient @@ -17,7 +17,7 @@ export async function promptExpiration(options: { const prompt = `Enter an expiration block sequence for the transaction. You can also enter 0 for no expiration, or leave blank to use the default. The current chain head is ${headSequence}` - const input = await ux.prompt(prompt, { required: false }) + const input = await inputPrompt(prompt) if (!input) { return } From 02e4cae732cb73b999906f32bd9ed3f1b6d563f5 Mon Sep 17 00:00:00 2001 From: andrea Date: Tue, 25 Jun 2024 19:23:10 -0700 Subject: [PATCH 42/81] Fast construction of `Transaction` when reading from the chain db Transactions that contains mints involve parsing assets, which in turn involve parsing public addresses of the owner for those assets. Parsing a public address is currently an expensive operation, because it involves elliptic curve point multiplications. Transactions containing mints are therefore harmful for the performance of wallet scans. To improve the performance of wallet scans, this commit: - introduces a new `PublicAddress::new_unchecked` (in Rust) constructor that speeds up construction of public addresses from trusted input; - adds a `skipValidation` parameter to `Transaction` (in TypeScript) to allow using the "unchecked" variants of constructors; - changes the chain db deserializer to read transactions using `skipValidation: true`. Note that, while the goal of this commit is to improve wallet scan performance, the impact of this commit is actually broader: it impacts all reads from the chain db. This is good for performance, but may be a concern for security and stability. The assumption is that if something is stored in the chain db, then it was previously validated and therefore it can be trusted. --- ironfish-rust-nodejs/index.d.ts | 2 +- ironfish-rust-nodejs/src/structs/asset.rs | 10 +++++-- ironfish-rust/src/assets/asset.rs | 14 +++++++++ ironfish-rust/src/keys/public_address.rs | 29 ++++++++++++------- .../src/blockchain/database/transactions.ts | 2 +- ironfish/src/memPool/memPool.test.ts | 4 +-- ironfish/src/primitives/transaction.ts | 6 ++-- ironfish/src/testUtilities/matchers/index.ts | 1 + .../src/testUtilities/matchers/transaction.ts | 19 ++++++++++++ 9 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 ironfish/src/testUtilities/matchers/transaction.ts diff --git a/ironfish-rust-nodejs/index.d.ts b/ironfish-rust-nodejs/index.d.ts index c7879d3b74..dd8e93485e 100644 --- a/ironfish-rust-nodejs/index.d.ts +++ b/ironfish-rust-nodejs/index.d.ts @@ -114,7 +114,7 @@ export class Asset { static nativeId(): Buffer id(): Buffer serialize(): Buffer - static deserialize(jsBytes: Buffer): NativeAsset + static deserialize(jsBytes: Buffer, skipValidation?: boolean | undefined | null): NativeAsset } export type NativeNoteEncrypted = NoteEncrypted export class NoteEncrypted { diff --git a/ironfish-rust-nodejs/src/structs/asset.rs b/ironfish-rust-nodejs/src/structs/asset.rs index 8e371dcc3e..1052f41c30 100644 --- a/ironfish-rust-nodejs/src/structs/asset.rs +++ b/ironfish-rust-nodejs/src/structs/asset.rs @@ -91,9 +91,15 @@ impl NativeAsset { } #[napi(factory)] - pub fn deserialize(js_bytes: JsBuffer) -> Result { + pub fn deserialize(js_bytes: JsBuffer, skip_validation: Option) -> Result { let bytes = js_bytes.into_value()?; - let asset = Asset::read(bytes.as_ref()).map_err(to_napi_err)?; + let skip_validation = skip_validation.unwrap_or(false); + let asset = if !skip_validation { + Asset::read(bytes.as_ref()) + } else { + Asset::read_unchecked(bytes.as_ref()) + } + .map_err(to_napi_err)?; Ok(NativeAsset { asset }) } diff --git a/ironfish-rust/src/assets/asset.rs b/ironfish-rust/src/assets/asset.rs index 3b1d02ca83..f1d4605435 100644 --- a/ironfish-rust/src/assets/asset.rs +++ b/ironfish-rust/src/assets/asset.rs @@ -134,6 +134,20 @@ impl Asset { Asset::new_with_nonce(creator, name, metadata, nonce) } + pub fn read_unchecked(mut reader: R) -> Result { + let creator = PublicAddress::read_unchecked(&mut reader)?; + + let mut name = [0; NAME_LENGTH]; + reader.read_exact(&mut name[..])?; + + let mut metadata = [0; METADATA_LENGTH]; + reader.read_exact(&mut metadata[..])?; + + let nonce = reader.read_u8()?; + + Asset::new_with_nonce(creator, name, metadata, nonce) + } + /// Stow the bytes of this struct in the given writer. pub fn write(&self, mut writer: W) -> Result<(), IronfishError> { self.creator.write(&mut writer)?; diff --git a/ironfish-rust/src/keys/public_address.rs b/ironfish-rust/src/keys/public_address.rs index d7af609462..4b9ea5f887 100644 --- a/ironfish-rust/src/keys/public_address.rs +++ b/ironfish-rust/src/keys/public_address.rs @@ -23,17 +23,18 @@ pub struct PublicAddress(pub(crate) SubgroupPoint); impl PublicAddress { /// Initialize a public address from its 32 byte representation. - pub fn new( - public_address_bytes: &[u8; PUBLIC_ADDRESS_SIZE], - ) -> Result { - assert!(public_address_bytes.len() == 32); - let public_address_non_prime = SubgroupPoint::from_bytes(public_address_bytes); - - if public_address_non_prime.is_some().into() { - Ok(PublicAddress(public_address_non_prime.unwrap())) - } else { - Err(IronfishError::new(IronfishErrorKind::InvalidPaymentAddress)) - } + pub fn new(bytes: &[u8; PUBLIC_ADDRESS_SIZE]) -> Result { + Option::from(SubgroupPoint::from_bytes(bytes)) + .map(PublicAddress) + .ok_or_else(|| IronfishError::new(IronfishErrorKind::InvalidPaymentAddress)) + } + + /// Initialize a public address from its 32 byte representation, without performing expensive + /// checks on the validity of the address. + pub fn new_unchecked(bytes: &[u8; PUBLIC_ADDRESS_SIZE]) -> Result { + Option::from(SubgroupPoint::from_bytes_unchecked(bytes)) + .map(PublicAddress) + .ok_or_else(|| IronfishError::new(IronfishErrorKind::InvalidPaymentAddress)) } /// Load a public address from a Read implementation (e.g: socket, file) @@ -43,6 +44,12 @@ impl PublicAddress { Self::new(&address_bytes) } + pub fn read_unchecked(reader: &mut R) -> Result { + let mut address_bytes = [0; PUBLIC_ADDRESS_SIZE]; + reader.read_exact(&mut address_bytes)?; + Self::new_unchecked(&address_bytes) + } + /// Initialize a public address from a sapling key. Typically constructed from /// SaplingKey::public_address() pub fn from_key(sapling_key: &SaplingKey) -> PublicAddress { diff --git a/ironfish/src/blockchain/database/transactions.ts b/ironfish/src/blockchain/database/transactions.ts index be891b94c6..3f489e2e2b 100644 --- a/ironfish/src/blockchain/database/transactions.ts +++ b/ironfish/src/blockchain/database/transactions.ts @@ -27,7 +27,7 @@ export class TransactionsValueEncoding implements IDatabaseEncoding { await chain.removeBlock(block.header.hash) expect(memPool.get(transaction.hash())).toBeDefined() - expect([...memPool.orderedTransactions()]).toContainEqual(transaction) + expect(memPool.orderedTransactions()).toContainEqual(transaction) expect(memPool.exists(minersFee.hash())).toBe(false) - expect([...memPool.orderedTransactions()]).not.toContainEqual(minersFee) + expect(memPool.orderedTransactions()).not.toContainEqual(minersFee) }) it('does not add back in transactions with overlapping nullifiers if fee is smaller', async () => { diff --git a/ironfish/src/primitives/transaction.ts b/ironfish/src/primitives/transaction.ts index 4f3c135796..b41c0e7573 100644 --- a/ironfish/src/primitives/transaction.ts +++ b/ironfish/src/primitives/transaction.ts @@ -62,7 +62,7 @@ export class Transaction { private transactionPosted: TransactionPosted | null = null private referenceCount = 0 - constructor(transactionPostedSerialized: Buffer) { + constructor(transactionPostedSerialized: Buffer, options?: { skipValidation?: boolean }) { this.transactionPostedSerialized = transactionPostedSerialized const reader = bufio.read(this.transactionPostedSerialized, true) @@ -109,14 +109,14 @@ export class Transaction { reader.seek(PROOF_LENGTH) // output note - return new NoteEncrypted(reader.readBytes(ENCRYPTED_NOTE_LENGTH, true)) + return new NoteEncrypted(reader.readBytes(ENCRYPTED_NOTE_LENGTH, true), options) }) this.mints = Array.from({ length: _mintsLength }, () => { // proof reader.seek(PROOF_LENGTH) - const asset = Asset.deserialize(reader.readBytes(ASSET_LENGTH)) + const asset = Asset.deserialize(reader.readBytes(ASSET_LENGTH), options?.skipValidation) const value = reader.readBigU64() let owner = null diff --git a/ironfish/src/testUtilities/matchers/index.ts b/ironfish/src/testUtilities/matchers/index.ts index f9b69a0db0..f39e133312 100644 --- a/ironfish/src/testUtilities/matchers/index.ts +++ b/ironfish/src/testUtilities/matchers/index.ts @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import './blockchain' +import './transaction' import './buffer' import './merkletree' import './string' diff --git a/ironfish/src/testUtilities/matchers/transaction.ts b/ironfish/src/testUtilities/matchers/transaction.ts new file mode 100644 index 0000000000..3af7297ba0 --- /dev/null +++ b/ironfish/src/testUtilities/matchers/transaction.ts @@ -0,0 +1,19 @@ +/* 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 { Transaction } from '../../primitives/transaction' + +function areTransactionsEqual(a: unknown, b: unknown): boolean | undefined { + const isATransaction = a instanceof Transaction + const isBTransaction = b instanceof Transaction + + if (isATransaction && isBTransaction) { + return a.equals(b) + } else if (!isATransaction && !isBTransaction) { + return undefined + } else { + return false + } +} + +expect.addEqualityTesters([areTransactionsEqual]) From 6143c205b86a94758f1af6e9b0bb800f6f330912 Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Thu, 11 Jul 2024 16:13:15 -0700 Subject: [PATCH 43/81] Upgrade to oclif 4.x (#5130) * Upgrade to oclif 4.x * Add nock as a direct dependency It looks like we were using this library which we had because it was being installed as an indirect dependency. That indirect dependency has apparently been removed, which was causing our usage of it to break. * Remove unused import --- ironfish-cli/bin/run | 5 +- ironfish-cli/package.json | 15 +- ironfish-cli/src/command.ts | 2 +- .../src/commands/mempool/transactions.ts | 5 +- ironfish-cli/src/commands/peers/banned.ts | 2 +- ironfish-cli/src/commands/peers/index.ts | 4 +- ironfish-cli/src/commands/start.ts | 6 +- ironfish-cli/src/commands/swim.ts | 4 +- ironfish-cli/src/ui/prompt.ts | 2 +- ironfish-cli/src/ui/table.ts | 2 +- ironfish/package.json | 1 + yarn.lock | 3551 ++++++++--------- 12 files changed, 1745 insertions(+), 1854 deletions(-) diff --git a/ironfish-cli/bin/run b/ironfish-cli/bin/run index 485f409e80..3b9901180c 100755 --- a/ironfish-cli/bin/run +++ b/ironfish-cli/bin/run @@ -24,7 +24,4 @@ if (Number(process.versions.node.split('.')[0]) < MIN_NODE_VERSION) { process.exit(1) } -require('@oclif/core') - .run() - .then(require('@oclif/core/flush')) - .catch(require('@oclif/core/handle')) +require('@oclif/core').execute({ dir: __dirname }) diff --git a/ironfish-cli/package.json b/ironfish-cli/package.json index 9841cd65a3..06fe43f4d7 100644 --- a/ironfish-cli/package.json +++ b/ironfish-cli/package.json @@ -23,8 +23,9 @@ "node": ">=18" }, "devDependencies": { - "@oclif/test": "2.1.0", + "@oclif/test": "4.0.4", "@types/blessed": "0.1.17", + "@types/cli-progress": "3.11.6", "@types/inquirer": "8.2.5", "@types/node": "18.11.16", "@types/tar": "6.1.1", @@ -34,7 +35,7 @@ "eslint-plugin-deprecation": "2.0.0", "jest": "29.7.0", "jest-jasmine2": "29.7.0", - "oclif": "3.7.2", + "oclif": "4.14.0", "rimraf": "^3.0.2", "ts-jest": "29.1.1", "tsc-watch": "4.2.9", @@ -61,11 +62,11 @@ "@ironfish/rust-nodejs": "2.4.0", "@ironfish/sdk": "2.4.1", "@ledgerhq/hw-transport-node-hid": "6.29.1", - "@oclif/core": "3.27.0", - "@oclif/plugin-autocomplete": "1.3.10", - "@oclif/plugin-help": "5.1.12", - "@oclif/plugin-not-found": "2.3.1", - "@oclif/plugin-warn-if-update-available": "2.0.40", + "@oclif/core": "4.0.11", + "@oclif/plugin-autocomplete": "3.1.6", + "@oclif/plugin-help": "6.2.5", + "@oclif/plugin-not-found": "3.2.10", + "@oclif/plugin-warn-if-update-available": "3.1.8", "@types/keccak": "3.0.4", "@types/tar": "6.1.1", "@zondax/ledger-ironfish": "0.1.2", diff --git a/ironfish-cli/src/command.ts b/ironfish-cli/src/command.ts index 5c2d3f5997..c1edd805cd 100644 --- a/ironfish-cli/src/command.ts +++ b/ironfish-cli/src/command.ts @@ -12,7 +12,7 @@ import { RpcConnectionError, } from '@ironfish/sdk' import { Command, Config } from '@oclif/core' -import { CLIError, ExitError } from '@oclif/core/lib/errors' +import { CLIError, ExitError } from '@oclif/core/errors' import { ConfigFlagKey, DataDirFlagKey, diff --git a/ironfish-cli/src/commands/mempool/transactions.ts b/ironfish-cli/src/commands/mempool/transactions.ts index e6c4f12e8d..a2d82b5829 100644 --- a/ironfish-cli/src/commands/mempool/transactions.ts +++ b/ironfish-cli/src/commands/mempool/transactions.ts @@ -2,8 +2,7 @@ * 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 { getFeeRate, GetMempoolTransactionResponse, MinMax, Transaction } from '@ironfish/sdk' -import { Flags } from '@oclif/core' -import { InferredFlags } from '@oclif/core/lib/interfaces' +import { Flags, Interfaces } from '@oclif/core' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' import { table, TableColumns, TableFlags } from '../../ui' @@ -152,7 +151,7 @@ type TransactionRow = { function renderTable( response: GetMempoolTransactionResponse[], - flags: InferredFlags, + flags: Interfaces.InferredFlags, ): string { const columns: TableColumns = { position: { diff --git a/ironfish-cli/src/commands/peers/banned.ts b/ironfish-cli/src/commands/peers/banned.ts index d8aef88067..28540c6af1 100644 --- a/ironfish-cli/src/commands/peers/banned.ts +++ b/ironfish-cli/src/commands/peers/banned.ts @@ -19,7 +19,7 @@ export class BannedCommand extends IronfishCommand { sort: { ...sort, exclusive: ['follow'], - }, + } as typeof sort, follow: Flags.boolean({ char: 'f', default: false, diff --git a/ironfish-cli/src/commands/peers/index.ts b/ironfish-cli/src/commands/peers/index.ts index 05bf983f19..3a46771f56 100644 --- a/ironfish-cli/src/commands/peers/index.ts +++ b/ironfish-cli/src/commands/peers/index.ts @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { GetPeersResponse, PromiseUtils } from '@ironfish/sdk' import { Flags } from '@oclif/core' -import { InferredFlags } from '@oclif/core/lib/interfaces' +import { Interfaces } from '@oclif/core' import blessed from 'blessed' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' @@ -94,7 +94,7 @@ export class ListCommand extends IronfishCommand { function renderTable( content: GetPeersResponse, - flags: InferredFlags, + flags: Interfaces.InferredFlags, ): string { let columns: TableColumns = { identity: { diff --git a/ironfish-cli/src/commands/start.ts b/ironfish-cli/src/commands/start.ts index b64ebd175a..44805b3ec9 100644 --- a/ironfish-cli/src/commands/start.ts +++ b/ironfish-cli/src/commands/start.ts @@ -44,9 +44,9 @@ export default class Start extends IronfishCommand { [VerboseFlagKey]: VerboseFlag, [ConfigFlagKey]: ConfigFlag, [DataDirFlagKey]: DataDirFlag, - [RpcUseIpcFlagKey]: { ...RpcUseIpcFlag, allowNo: true }, - [RpcUseTcpFlagKey]: { ...RpcUseTcpFlag, allowNo: true }, - [RpcUseHttpFlagKey]: { ...RpcUseHttpFlag, allowNo: true }, + [RpcUseIpcFlagKey]: { ...RpcUseIpcFlag, allowNo: true } as typeof RpcUseIpcFlag, + [RpcUseTcpFlagKey]: { ...RpcUseTcpFlag, allowNo: true } as typeof RpcUseTcpFlag, + [RpcUseHttpFlagKey]: { ...RpcUseHttpFlag, allowNo: true } as typeof RpcUseHttpFlag, [RpcTcpTlsFlagKey]: RpcTcpTlsFlag, [RpcAuthFlagKey]: RpcAuthFlag, [RpcTcpHostFlagKey]: RpcTcpHostFlag, diff --git a/ironfish-cli/src/commands/swim.ts b/ironfish-cli/src/commands/swim.ts index 7b9d75f720..ef81c71ac0 100644 --- a/ironfish-cli/src/commands/swim.ts +++ b/ironfish-cli/src/commands/swim.ts @@ -1,7 +1,7 @@ /* 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 { ux } from '@oclif/core' +import { PromiseUtils } from '@ironfish/sdk' import { IronfishCommand } from '../command' import { ONE_FISH_IMAGE, TWO_FISH_IMAGE } from '../images' @@ -40,7 +40,7 @@ export default class SwimCommand extends IronfishCommand { console.clear() this.renderPixels(pixels) this.log('The hex fish are coming...') - await ux.wait(32) + await PromiseUtils.sleep(32) } // eslint-disable-next-line no-console diff --git a/ironfish-cli/src/ui/prompt.ts b/ironfish-cli/src/ui/prompt.ts index dc272c546c..d0b201497e 100644 --- a/ironfish-cli/src/ui/prompt.ts +++ b/ironfish-cli/src/ui/prompt.ts @@ -48,7 +48,7 @@ export async function confirmOrQuit(message?: string, confirm?: boolean): Promis const confirmed = await confirmPrompt(message || 'Do you confirm?') if (!confirmed) { - ux.log('Operation aborted.') + ux.stdout('Operation aborted.') ux.exit(0) } } diff --git a/ironfish-cli/src/ui/table.ts b/ironfish-cli/src/ui/table.ts index 5d18116e3e..c105dd4dc4 100644 --- a/ironfish-cli/src/ui/table.ts +++ b/ironfish-cli/src/ui/table.ts @@ -79,7 +79,7 @@ class Table> { extended: options.extended || false, 'no-header': options['no-header'], output: options.csv ? 'csv' : options.output, - printLine: options.printLine ?? ux.log.bind(ux), + printLine: options.printLine ?? ux.stdout.bind(ux), sort: options.sort, } } diff --git a/ironfish/package.json b/ironfish/package.json index e5dae413cb..9699fb6145 100644 --- a/ironfish/package.json +++ b/ironfish/package.json @@ -41,6 +41,7 @@ "leveldown": "6.1.1", "levelup": "4.4.0", "lodash": "4.17.21", + "nock": "13.5.4", "node-datachannel": "0.8.0", "node-forge": "1.3.1", "parse-json": "5.2.0", diff --git a/yarn.lock b/yarn.lock index b94a91a5a6..bf681b4abe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,6 +15,648 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" +"@aws-crypto/crc32@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-5.2.0.tgz#cfcc22570949c98c6689cfcbd2d693d36cdae2e1" + integrity sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/crc32c@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz#4e34aab7f419307821509a98b9b08e84e0c1917e" + integrity sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/sha1-browser@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz#b0ee2d2821d3861f017e965ef3b4cb38e3b6a0f4" + integrity sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg== + dependencies: + "@aws-crypto/supports-web-crypto" "^5.2.0" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-crypto/sha256-browser@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e" + integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw== + dependencies: + "@aws-crypto/sha256-js" "^5.2.0" + "@aws-crypto/supports-web-crypto" "^5.2.0" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042" + integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/supports-web-crypto@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz#a1e399af29269be08e695109aa15da0a07b5b5fb" + integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== + dependencies: + tslib "^2.6.2" + +"@aws-crypto/util@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" + integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== + dependencies: + "@aws-sdk/types" "^3.222.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-cloudfront@^3.609.0": + version "3.613.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-cloudfront/-/client-cloudfront-3.613.0.tgz#0bdbdf7b61f03e8de4287879eaf0c7a28e779e5a" + integrity sha512-HLdRyouDGd+4OM0BX5u7JgF26e7YmZpdB3Xb3RZeHkAllkjP3O+GnEBPVVNtaJCtvTD2Tspv6/upjDF60b9SNg== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/client-sso-oidc" "3.613.0" + "@aws-sdk/client-sts" "3.613.0" + "@aws-sdk/core" "3.609.0" + "@aws-sdk/credential-provider-node" "3.613.0" + "@aws-sdk/middleware-host-header" "3.609.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.609.0" + "@aws-sdk/middleware-user-agent" "3.609.0" + "@aws-sdk/region-config-resolver" "3.609.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.609.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.609.0" + "@aws-sdk/xml-builder" "3.609.0" + "@smithy/config-resolver" "^3.0.4" + "@smithy/core" "^2.2.4" + "@smithy/fetch-http-handler" "^3.2.0" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.3" + "@smithy/middleware-endpoint" "^3.0.4" + "@smithy/middleware-retry" "^3.0.7" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.3" + "@smithy/node-http-handler" "^3.1.1" + "@smithy/protocol-http" "^4.0.3" + "@smithy/smithy-client" "^3.1.5" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.7" + "@smithy/util-defaults-mode-node" "^3.0.7" + "@smithy/util-endpoints" "^2.0.4" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-stream" "^3.0.5" + "@smithy/util-utf8" "^3.0.0" + "@smithy/util-waiter" "^3.1.2" + tslib "^2.6.2" + +"@aws-sdk/client-s3@^3.609.0": + version "3.613.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.613.0.tgz#780731af58c67f61aadb86a86f8896e0f3465973" + integrity sha512-JK0yjzZFa+/qVZawHasCG4yEEA7ITpBtNsn9ri7qUZDfSWxDDJpKgHI/ZWd4QXw3SLlBG9hoj+eNSroJXuv+hQ== + dependencies: + "@aws-crypto/sha1-browser" "5.2.0" + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/client-sso-oidc" "3.613.0" + "@aws-sdk/client-sts" "3.613.0" + "@aws-sdk/core" "3.609.0" + "@aws-sdk/credential-provider-node" "3.613.0" + "@aws-sdk/middleware-bucket-endpoint" "3.609.0" + "@aws-sdk/middleware-expect-continue" "3.609.0" + "@aws-sdk/middleware-flexible-checksums" "3.609.0" + "@aws-sdk/middleware-host-header" "3.609.0" + "@aws-sdk/middleware-location-constraint" "3.609.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.609.0" + "@aws-sdk/middleware-sdk-s3" "3.609.0" + "@aws-sdk/middleware-signing" "3.609.0" + "@aws-sdk/middleware-ssec" "3.609.0" + "@aws-sdk/middleware-user-agent" "3.609.0" + "@aws-sdk/region-config-resolver" "3.609.0" + "@aws-sdk/signature-v4-multi-region" "3.609.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.609.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.609.0" + "@aws-sdk/xml-builder" "3.609.0" + "@smithy/config-resolver" "^3.0.4" + "@smithy/core" "^2.2.4" + "@smithy/eventstream-serde-browser" "^3.0.4" + "@smithy/eventstream-serde-config-resolver" "^3.0.3" + "@smithy/eventstream-serde-node" "^3.0.4" + "@smithy/fetch-http-handler" "^3.2.0" + "@smithy/hash-blob-browser" "^3.1.2" + "@smithy/hash-node" "^3.0.3" + "@smithy/hash-stream-node" "^3.1.2" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/md5-js" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.3" + "@smithy/middleware-endpoint" "^3.0.4" + "@smithy/middleware-retry" "^3.0.7" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.3" + "@smithy/node-http-handler" "^3.1.1" + "@smithy/protocol-http" "^4.0.3" + "@smithy/smithy-client" "^3.1.5" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.7" + "@smithy/util-defaults-mode-node" "^3.0.7" + "@smithy/util-endpoints" "^2.0.4" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-stream" "^3.0.5" + "@smithy/util-utf8" "^3.0.0" + "@smithy/util-waiter" "^3.1.2" + tslib "^2.6.2" + +"@aws-sdk/client-sso-oidc@3.613.0": + version "3.613.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.613.0.tgz#1c98f0fb1254692a9884534aa73c74cff04034e5" + integrity sha512-VINgHA30f6Itjtj6ZAxkx86XhyFYa7UBfv2Ju+9QGcAr2Y3HU+Mh9g6QaTwDqIM5QG6Pgss24NaAItWGJHFf5A== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.609.0" + "@aws-sdk/credential-provider-node" "3.613.0" + "@aws-sdk/middleware-host-header" "3.609.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.609.0" + "@aws-sdk/middleware-user-agent" "3.609.0" + "@aws-sdk/region-config-resolver" "3.609.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.609.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.609.0" + "@smithy/config-resolver" "^3.0.4" + "@smithy/core" "^2.2.4" + "@smithy/fetch-http-handler" "^3.2.0" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.3" + "@smithy/middleware-endpoint" "^3.0.4" + "@smithy/middleware-retry" "^3.0.7" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.3" + "@smithy/node-http-handler" "^3.1.1" + "@smithy/protocol-http" "^4.0.3" + "@smithy/smithy-client" "^3.1.5" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.7" + "@smithy/util-defaults-mode-node" "^3.0.7" + "@smithy/util-endpoints" "^2.0.4" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-sso@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.609.0.tgz#2a99166694b64947ba5b7453f057772bd3bba5b8" + integrity sha512-gqXGFDkIpKHCKAbeJK4aIDt3tiwJ26Rf5Tqw9JS6BYXsdMeOB8FTzqD9R+Yc1epHd8s5L94sdqXT5PapgxFZrg== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.609.0" + "@aws-sdk/middleware-host-header" "3.609.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.609.0" + "@aws-sdk/middleware-user-agent" "3.609.0" + "@aws-sdk/region-config-resolver" "3.609.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.609.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.609.0" + "@smithy/config-resolver" "^3.0.4" + "@smithy/core" "^2.2.4" + "@smithy/fetch-http-handler" "^3.2.0" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.3" + "@smithy/middleware-endpoint" "^3.0.4" + "@smithy/middleware-retry" "^3.0.7" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.3" + "@smithy/node-http-handler" "^3.1.1" + "@smithy/protocol-http" "^4.0.3" + "@smithy/smithy-client" "^3.1.5" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.7" + "@smithy/util-defaults-mode-node" "^3.0.7" + "@smithy/util-endpoints" "^2.0.4" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-sts@3.613.0": + version "3.613.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.613.0.tgz#b648592d79dba0d4badaeacb4f33b3a2b158d0ca" + integrity sha512-S+KvQI4XEivY3vyIY+IPY7Fw8vFvX/q3pkNC9qEhnAs+/w7vT6vhVBHsaugYVlsMuNtNvmyc8P+Q/gzOEtLCTw== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/client-sso-oidc" "3.613.0" + "@aws-sdk/core" "3.609.0" + "@aws-sdk/credential-provider-node" "3.613.0" + "@aws-sdk/middleware-host-header" "3.609.0" + "@aws-sdk/middleware-logger" "3.609.0" + "@aws-sdk/middleware-recursion-detection" "3.609.0" + "@aws-sdk/middleware-user-agent" "3.609.0" + "@aws-sdk/region-config-resolver" "3.609.0" + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.609.0" + "@aws-sdk/util-user-agent-browser" "3.609.0" + "@aws-sdk/util-user-agent-node" "3.609.0" + "@smithy/config-resolver" "^3.0.4" + "@smithy/core" "^2.2.4" + "@smithy/fetch-http-handler" "^3.2.0" + "@smithy/hash-node" "^3.0.3" + "@smithy/invalid-dependency" "^3.0.3" + "@smithy/middleware-content-length" "^3.0.3" + "@smithy/middleware-endpoint" "^3.0.4" + "@smithy/middleware-retry" "^3.0.7" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/node-config-provider" "^3.1.3" + "@smithy/node-http-handler" "^3.1.1" + "@smithy/protocol-http" "^4.0.3" + "@smithy/smithy-client" "^3.1.5" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.7" + "@smithy/util-defaults-mode-node" "^3.0.7" + "@smithy/util-endpoints" "^2.0.4" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/core@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.609.0.tgz#4c3994cd341452d1ef1a8b5e81a16442a7422287" + integrity sha512-ptqw+DTxLr01+pKjDUuo53SEDzI+7nFM3WfQaEo0yhDg8vWw8PER4sWj1Ysx67ksctnZesPUjqxd5SHbtdBxiA== + dependencies: + "@smithy/core" "^2.2.4" + "@smithy/protocol-http" "^4.0.3" + "@smithy/signature-v4" "^3.1.2" + "@smithy/smithy-client" "^3.1.5" + "@smithy/types" "^3.3.0" + fast-xml-parser "4.2.5" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-env@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.609.0.tgz#b3f32e5a8ff8b541e151eadadfb60283aa3d835e" + integrity sha512-v69ZCWcec2iuV9vLVJMa6fAb5xwkzN4jYIT8yjo2c4Ia/j976Q+TPf35Pnz5My48Xr94EFcaBazrWedF+kwfuQ== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-http@3.613.0": + version "3.613.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.613.0.tgz#bb7987f8f78bc3372c56aadb52e5ac965bf168ad" + integrity sha512-MCiUFxowFzprzIXFXsqbp/3DViJ7nFmBW+XJkoRQWqNmThbkz/E8sb40WmL9UFdZHJph2KDjzABKYH5f0lHZaA== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/fetch-http-handler" "^3.2.0" + "@smithy/node-http-handler" "^3.1.1" + "@smithy/property-provider" "^3.1.3" + "@smithy/protocol-http" "^4.0.3" + "@smithy/smithy-client" "^3.1.5" + "@smithy/types" "^3.3.0" + "@smithy/util-stream" "^3.0.5" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-ini@3.613.0": + version "3.613.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.613.0.tgz#11736835f330571d15d240bc6d7812c826d845b0" + integrity sha512-scHV7K0YpllYMWxPnqxssWU+7S3WNXH1m5Rw8Ax96pfcfnaoatiWXps2XSSdGlChdF9gNVnewjRKFOTLyyzdAw== + dependencies: + "@aws-sdk/credential-provider-env" "3.609.0" + "@aws-sdk/credential-provider-http" "3.613.0" + "@aws-sdk/credential-provider-process" "3.609.0" + "@aws-sdk/credential-provider-sso" "3.609.0" + "@aws-sdk/credential-provider-web-identity" "3.609.0" + "@aws-sdk/types" "3.609.0" + "@smithy/credential-provider-imds" "^3.1.3" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-node@3.613.0": + version "3.613.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.613.0.tgz#9d9a03658e9b5e657fd3b75955e58971d62c2e0c" + integrity sha512-n3yd0CDuUKcQFhjRLAQfQpZyZ2ddrHC7QOKQqE+Fkx+Fs5zoG+NRLK1EBkBW/G9zk8Ck4+rG3OOI3CuNpJ2PCw== + dependencies: + "@aws-sdk/credential-provider-env" "3.609.0" + "@aws-sdk/credential-provider-http" "3.613.0" + "@aws-sdk/credential-provider-ini" "3.613.0" + "@aws-sdk/credential-provider-process" "3.609.0" + "@aws-sdk/credential-provider-sso" "3.609.0" + "@aws-sdk/credential-provider-web-identity" "3.609.0" + "@aws-sdk/types" "3.609.0" + "@smithy/credential-provider-imds" "^3.1.3" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-process@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.609.0.tgz#2bfa160eec4be8532a45061810466ee3462ce240" + integrity sha512-Ux35nGOSJKZWUIM3Ny0ROZ8cqPRUEkh+tR3X2o9ydEbFiLq3eMMyEnHJqx4EeUjLRchidlm4CCid9GxMe5/gdw== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-sso@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.609.0.tgz#94da403a000060700a34ee62fcf119fd4cacf167" + integrity sha512-oQPGDKMMIxjvTcm86g07RPYeC7mCNk+29dPpY15ZAPRpAF7F0tircsC3wT9fHzNaKShEyK5LuI5Kg/uxsdy+Iw== + dependencies: + "@aws-sdk/client-sso" "3.609.0" + "@aws-sdk/token-providers" "3.609.0" + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-web-identity@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.609.0.tgz#d29222d6894347ee89c781ea090d388656df1d2a" + integrity sha512-U+PG8NhlYYF45zbr1km3ROtBMYqyyj/oK8NRp++UHHeuavgrP+4wJ4wQnlEaKvJBjevfo3+dlIBcaeQ7NYejWg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-bucket-endpoint@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.609.0.tgz#f6f1e366c1816292d6e78866653c6e7122b7932f" + integrity sha512-QhHRfr4e7FqaMUAnOAFdQVOR3yDLw40i1IZPo+TeiKyev9LEyYEX2l6DbdaIwAztofOpAxfFNj/IJ0V/efzz/w== + dependencies: + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-arn-parser" "3.568.0" + "@smithy/node-config-provider" "^3.1.3" + "@smithy/protocol-http" "^4.0.3" + "@smithy/types" "^3.3.0" + "@smithy/util-config-provider" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-expect-continue@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.609.0.tgz#89af76f115aa5fadd5a82fe4e95a64cb15150517" + integrity sha512-+zeg//mSer4JZRxOB/4mUOMUJyuYPwATnIC5moBB8P8Xe+mJaVRFy8qlCtzYNj2TycnlsBPzTK0j7P1yvDh97w== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/protocol-http" "^4.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-flexible-checksums@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.609.0.tgz#3bcd54d64e65808f2053d5a38053625cacb5464a" + integrity sha512-TJ4WE+ehT+qcrhr7/yJCzmJJPmUoPPWIbCnFzqGxauH/dpVBCslmd1vZg3h2VnfRiaDkc6f68dqYVc29CaurhQ== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@aws-crypto/crc32c" "5.2.0" + "@aws-sdk/types" "3.609.0" + "@smithy/is-array-buffer" "^3.0.0" + "@smithy/protocol-http" "^4.0.3" + "@smithy/types" "^3.3.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-host-header@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.609.0.tgz#844302cb905e4d09b9a1ea4bfa96729833068913" + integrity sha512-iTKfo158lc4jLDfYeZmYMIBHsn8m6zX+XB6birCSNZ/rrlzAkPbGE43CNdKfvjyWdqgLMRXF+B+OcZRvqhMXPQ== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/protocol-http" "^4.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-location-constraint@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.609.0.tgz#7ed82d71e5ddcd50683ef2bbde10d1cc2492057e" + integrity sha512-xzsdoTkszGVqGVPjUmgoP7TORiByLueMHieI1fhQL888WPdqctwAx3ES6d/bA9Q/i8jnc6hs+Fjhy8UvBTkE9A== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-logger@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz#ed44d201f091b8bac908cbf14724c7a4d492553f" + integrity sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-recursion-detection@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.609.0.tgz#b7b869aaeac021a43dbea1435eaea81e5d2460b1" + integrity sha512-6sewsYB7/o/nbUfA99Aa/LokM+a/u4Wpm/X2o0RxOsDtSB795ObebLJe2BxY5UssbGaWkn7LswyfvrdZNXNj1w== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/protocol-http" "^4.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-sdk-s3@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.609.0.tgz#a743bd63adf786c7c6d4b9102946b67a5032d1f4" + integrity sha512-kvwjL6OJFhAGWoYaIWR7HmILjiVk6xVj6QEU6qZMA7FtGgvlKi4pLfs8Of+hQqo+2TEhUoxG/5t6WqwB8uxjsw== + dependencies: + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-arn-parser" "3.568.0" + "@smithy/node-config-provider" "^3.1.3" + "@smithy/protocol-http" "^4.0.3" + "@smithy/signature-v4" "^3.1.2" + "@smithy/smithy-client" "^3.1.5" + "@smithy/types" "^3.3.0" + "@smithy/util-config-provider" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-signing@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.609.0.tgz#7e5c4e70302bf87a7aa3dfde83ec1b387bf819f0" + integrity sha512-2w3dBLjQVKIajYzokO4hduq8/0hSMUYHHmIo1Kdl+MSY8uwRBt12bLL6pyreobTcRMxizvn2ph/CQ9I1ST/WGQ== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/protocol-http" "^4.0.3" + "@smithy/signature-v4" "^3.1.2" + "@smithy/types" "^3.3.0" + "@smithy/util-middleware" "^3.0.3" + tslib "^2.6.2" + +"@aws-sdk/middleware-ssec@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.609.0.tgz#b87a8bc6133f3f6bdc6801183d0f9dad3f93cf9f" + integrity sha512-GZSD1s7+JswWOTamVap79QiDaIV7byJFssBW68GYjyRS5EBjNfwA/8s+6uE6g39R3ojyTbYOmvcANoZEhSULXg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-user-agent@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.609.0.tgz#eb3b7c604817be42f7ecd97988dda69a22e6011b" + integrity sha512-nbq7MXRmeXm4IDqh+sJRAxGPAq0OfGmGIwKvJcw66hLoG8CmhhVMZmIAEBDFr57S+YajGwnLLRt+eMI05MMeVA== + dependencies: + "@aws-sdk/types" "3.609.0" + "@aws-sdk/util-endpoints" "3.609.0" + "@smithy/protocol-http" "^4.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/region-config-resolver@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.609.0.tgz#68fe568d1c69f35f7fa3d66f718bd5751b1debda" + integrity sha512-lMHBG8zg9GWYBc9/XVPKyuAUd7iKqfPP7z04zGta2kGNOKbUTeqmAdc1gJGku75p4kglIPlGBorOxti8DhRmKw== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/node-config-provider" "^3.1.3" + "@smithy/types" "^3.3.0" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + tslib "^2.6.2" + +"@aws-sdk/signature-v4-multi-region@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.609.0.tgz#c88c4a25713bd50b7b253d611e3d2473258087eb" + integrity sha512-FJs0BxVMyYOKNu7nzFI1kehfgWoYmdto5B8BSS29geUACF7jlOoeCfNZWVrnMjvAxVlSQ5O7Mr575932BnsycA== + dependencies: + "@aws-sdk/middleware-sdk-s3" "3.609.0" + "@aws-sdk/types" "3.609.0" + "@smithy/protocol-http" "^4.0.3" + "@smithy/signature-v4" "^3.1.2" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/token-providers@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.609.0.tgz#cfa9cdc84fefe71277c7d44b08b09f42c16c1d66" + integrity sha512-WvhW/7XSf+H7YmtiIigQxfDVZVZI7mbKikQ09YpzN7FeN3TmYib1+0tB+EE9TbICkwssjiFc71FEBEh4K9grKQ== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/types@3.609.0", "@aws-sdk/types@^3.222.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.609.0.tgz#06b39d799c9f197a7b43670243e8e78a3bf7d6a5" + integrity sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/util-arn-parser@3.568.0": + version "3.568.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz#6a19a8c6bbaa520b6be1c278b2b8c17875b91527" + integrity sha512-XUKJWWo+KOB7fbnPP0+g/o5Ulku/X53t7i/h+sPHr5xxYTJJ9CYnbToo95mzxe7xWvkLrsNtJ8L+MnNn9INs2w== + dependencies: + tslib "^2.6.2" + +"@aws-sdk/util-endpoints@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.609.0.tgz#e02d3fce2f999d750828dacf9f37289a1a48f6c9" + integrity sha512-Rh+3V8dOvEeE1aQmUy904DYWtLUEJ7Vf5XBPlQ6At3pBhp+zpXbsnpZzVL33c8lW1xfj6YPwtO6gOeEsl1juCQ== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + "@smithy/util-endpoints" "^2.0.4" + tslib "^2.6.2" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.568.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz#2acc4b2236af0d7494f7e517401ba6b3c4af11ff" + integrity sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig== + dependencies: + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-browser@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz#aa15421b2e32ae8bc589dac2bd6e8969832ce588" + integrity sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/types" "^3.3.0" + bowser "^2.11.0" + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-node@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.609.0.tgz#f8270517b2961cbf627e4e8fb6338ad153db44bb" + integrity sha512-DlZBwQ/HkZyf3pOWc7+wjJRk5R7x9YxHhs2szHwtv1IW30KMabjjjX0GMlGJ9LLkBHkbaaEY/w9Tkj12XRLhRg== + dependencies: + "@aws-sdk/types" "3.609.0" + "@smithy/node-config-provider" "^3.1.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@aws-sdk/xml-builder@3.609.0": + version "3.609.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.609.0.tgz#eeb3d5cde000a23cfeeefe0354b6193440dc7d87" + integrity sha512-l9XxNcA4HX98rwCC2/KoiWcmEiRfZe4G+mYwDbCFT87JIMj6GBhLDkAzr/W8KAaA2IDr8Vc6J8fZPgVulxxfMA== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13": version "7.22.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" @@ -270,13 +912,6 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.21.0": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" - integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== - dependencies: - regenerator-runtime "^0.14.0" - "@babel/template@^7.22.15", "@babel/template@^7.3.3": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" @@ -422,6 +1057,64 @@ resolved "https://registry.yarnpkg.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz#98c23c950a3d9b6c8f0daed06da6c3af06981340" integrity sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q== +"@inquirer/confirm@^3.1.11", "@inquirer/confirm@^3.1.14": + version "3.1.14" + resolved "https://registry.yarnpkg.com/@inquirer/confirm/-/confirm-3.1.14.tgz#b50a156f2cc0a6f874f2d2ab1739e988fbf950f4" + integrity sha512-nbLSX37b2dGPtKWL3rPuR/5hOuD30S+pqJ/MuFiUEgN6GiMs8UMxiurKAMDzKt6C95ltjupa8zH6+3csXNHWpA== + dependencies: + "@inquirer/core" "^9.0.2" + "@inquirer/type" "^1.4.0" + +"@inquirer/core@^9.0.2": + version "9.0.2" + resolved "https://registry.yarnpkg.com/@inquirer/core/-/core-9.0.2.tgz#8be8782266f00129acb5c804537d1231b2fe3ac6" + integrity sha512-nguvH3TZar3ACwbytZrraRTzGqyxJfYJwv+ZwqZNatAosdWQMP1GV8zvmkNlBe2JeZSaw0WYBHZk52pDpWC9qA== + dependencies: + "@inquirer/figures" "^1.0.3" + "@inquirer/type" "^1.4.0" + "@types/mute-stream" "^0.0.4" + "@types/node" "^20.14.9" + "@types/wrap-ansi" "^3.0.0" + ansi-escapes "^4.3.2" + cli-spinners "^2.9.2" + cli-width "^4.1.0" + mute-stream "^1.0.0" + signal-exit "^4.1.0" + strip-ansi "^6.0.1" + wrap-ansi "^6.2.0" + yoctocolors-cjs "^2.1.2" + +"@inquirer/figures@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@inquirer/figures/-/figures-1.0.3.tgz#1227cc980f88e6d6ab85abadbf164f5038041edd" + integrity sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw== + +"@inquirer/input@^2.1.9": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@inquirer/input/-/input-2.2.1.tgz#cb795ab12f25cc8c6eeb6f51f04c71a70e4067c8" + integrity sha512-Yl1G6h7qWydzrJwqN777geeJVaAFL5Ly83aZlw4xHf8Z/BoTMfKRheyuMaQwOG7LQ4e5nQP7PxXdEg4SzQ+OKw== + dependencies: + "@inquirer/core" "^9.0.2" + "@inquirer/type" "^1.4.0" + +"@inquirer/select@^2.3.10": + version "2.3.10" + resolved "https://registry.yarnpkg.com/@inquirer/select/-/select-2.3.10.tgz#4491805435984726c75f89e8f810ddb1fe503123" + integrity sha512-rr7iR0Zj1YFfgM8IUGimPD9Yukd+n/U63CnYT9kdum6DbRXtMxR45rrreP+EA9ixCnShr+W4xj7suRxC1+8t9g== + dependencies: + "@inquirer/core" "^9.0.2" + "@inquirer/figures" "^1.0.3" + "@inquirer/type" "^1.4.0" + ansi-escapes "^4.3.2" + yoctocolors-cjs "^2.1.2" + +"@inquirer/type@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-1.4.0.tgz#3dd0c8f78c0548bbc18b9c07af16a86c4007e1f0" + integrity sha512-AjOqykVyjdJQvtfkNDGUyMYGF8xN50VUxftCQWsOyIo4DFRLr6VQhW0VItGI1JIyQGCGgIpKa7hMMwNhZb4OIw== + dependencies: + mute-stream "^1.0.0" + "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz#291c227e93fd407a96ecd59879a35809120e432b" @@ -1704,44 +2397,6 @@ treeverse "^2.0.0" walk-up-path "^1.0.0" -"@npmcli/arborist@^4.0.4": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@npmcli/arborist/-/arborist-4.3.1.tgz#a08cddce3339882f688c1dea1651f6971e781c44" - integrity sha512-yMRgZVDpwWjplorzt9SFSaakWx6QIK248Nw4ZFgkrAy/GvJaFRaSZzE6nD7JBK5r8g/+PTxFq5Wj/sfciE7x+A== - dependencies: - "@isaacs/string-locale-compare" "^1.1.0" - "@npmcli/installed-package-contents" "^1.0.7" - "@npmcli/map-workspaces" "^2.0.0" - "@npmcli/metavuln-calculator" "^2.0.0" - "@npmcli/move-file" "^1.1.0" - "@npmcli/name-from-folder" "^1.0.1" - "@npmcli/node-gyp" "^1.0.3" - "@npmcli/package-json" "^1.0.1" - "@npmcli/run-script" "^2.0.0" - bin-links "^3.0.0" - cacache "^15.0.3" - common-ancestor-path "^1.0.1" - json-parse-even-better-errors "^2.3.1" - json-stringify-nice "^1.1.4" - mkdirp "^1.0.4" - mkdirp-infer-owner "^2.0.0" - npm-install-checks "^4.0.0" - npm-package-arg "^8.1.5" - npm-pick-manifest "^6.1.0" - npm-registry-fetch "^12.0.1" - pacote "^12.0.2" - parse-conflict-json "^2.0.1" - proc-log "^1.0.0" - promise-all-reject-late "^1.0.0" - promise-call-limit "^1.0.1" - read-package-json-fast "^2.0.2" - readdir-scoped-modules "^1.1.0" - rimraf "^3.0.2" - semver "^7.3.5" - ssri "^8.0.1" - treeverse "^1.0.4" - walk-up-path "^1.0.0" - "@npmcli/fs@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.0.0.tgz#589612cfad3a6ea0feafcb901d29c63fd52db09f" @@ -1758,20 +2413,6 @@ "@gar/promisify" "^1.1.3" semver "^7.3.5" -"@npmcli/git@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6" - integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw== - dependencies: - "@npmcli/promise-spawn" "^1.3.2" - lru-cache "^6.0.0" - mkdirp "^1.0.4" - npm-pick-manifest "^6.1.1" - promise-inflight "^1.0.1" - promise-retry "^2.0.1" - semver "^7.3.5" - which "^2.0.2" - "@npmcli/git@^3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-3.0.2.tgz#5c5de6b4d70474cf2d09af149ce42e4e1dacb931" @@ -1787,7 +2428,7 @@ semver "^7.3.5" which "^2.0.2" -"@npmcli/installed-package-contents@^1.0.6", "@npmcli/installed-package-contents@^1.0.7": +"@npmcli/installed-package-contents@^1.0.7": version "1.0.7" resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa" integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw== @@ -1795,16 +2436,6 @@ npm-bundled "^1.1.1" npm-normalize-package-bin "^1.0.1" -"@npmcli/map-workspaces@^2.0.0": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-2.0.2.tgz#dfc87ced615afeb98a081da2aa9bba072bf6712d" - integrity sha512-ED54EslGsHFWBPN5x8JAOszuWywuoXYSi9E3HQRsgVkWnqsdTBJDSM4IFMRwmmBUbCHAxmP3wGLu1WMm4fhrOw== - dependencies: - "@npmcli/name-from-folder" "^1.0.1" - glob "^7.2.0" - minimatch "^5.0.1" - read-package-json-fast "^2.0.3" - "@npmcli/map-workspaces@^2.0.3": version "2.0.4" resolved "https://registry.yarnpkg.com/@npmcli/map-workspaces/-/map-workspaces-2.0.4.tgz#9e5e8ab655215a262aefabf139782b894e0504fc" @@ -1815,16 +2446,6 @@ minimatch "^5.0.1" read-package-json-fast "^2.0.3" -"@npmcli/metavuln-calculator@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/metavuln-calculator/-/metavuln-calculator-2.0.0.tgz#70937b8b5a5cad5c588c8a7b38c4a8bd6f62c84c" - integrity sha512-VVW+JhWCKRwCTE+0xvD6p3uV4WpqocNYYtzyvenqL/u1Q3Xx6fGTJ+6UoIoii07fbuEO9U3IIyuGY0CYHDv1sg== - dependencies: - cacache "^15.0.5" - json-parse-even-better-errors "^2.3.1" - pacote "^12.0.0" - semver "^7.3.2" - "@npmcli/metavuln-calculator@^3.0.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@npmcli/metavuln-calculator/-/metavuln-calculator-3.1.1.tgz#9359bd72b400f8353f6a28a25c8457b562602622" @@ -1835,7 +2456,7 @@ pacote "^13.0.3" semver "^7.3.5" -"@npmcli/move-file@^1.0.1", "@npmcli/move-file@^1.1.0", "@npmcli/move-file@^1.1.2": +"@npmcli/move-file@^1.0.1", "@npmcli/move-file@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== @@ -1856,23 +2477,11 @@ resolved "https://registry.yarnpkg.com/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz#77ecd0a4fcb772ba6fe927e2e2e155fbec2e6b1a" integrity sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA== -"@npmcli/node-gyp@^1.0.2", "@npmcli/node-gyp@^1.0.3": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz#a912e637418ffc5f2db375e93b85837691a43a33" - integrity sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA== - "@npmcli/node-gyp@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz#8c20e53e34e9078d18815c1d2dda6f2420d75e35" integrity sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A== -"@npmcli/package-json@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-1.0.1.tgz#1ed42f00febe5293c3502fd0ef785647355f6e89" - integrity sha512-y6jnu76E9C23osz8gEMBayZmaZ69vFOIk8vR1FJL/wbEJ54+9aVG9rLTjQKSXfgYZEr50nw1txBBFfBZZe+bYg== - dependencies: - json-parse-even-better-errors "^2.3.1" - "@npmcli/package-json@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-2.0.0.tgz#3bbcf4677e21055adbe673d9f08c9f9cde942e4a" @@ -1880,13 +2489,6 @@ dependencies: json-parse-even-better-errors "^2.3.1" -"@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5" - integrity sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg== - dependencies: - infer-owner "^1.0.4" - "@npmcli/promise-spawn@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz#53283b5f18f855c6925f23c24e67c911501ef573" @@ -1894,16 +2496,6 @@ dependencies: infer-owner "^1.0.4" -"@npmcli/run-script@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-2.0.0.tgz#9949c0cab415b17aaac279646db4f027d6f1e743" - integrity sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig== - dependencies: - "@npmcli/node-gyp" "^1.0.2" - "@npmcli/promise-spawn" "^1.3.2" - node-gyp "^8.2.0" - read-package-json-fast "^2.0.1" - "@npmcli/run-script@^4.1.0", "@npmcli/run-script@^4.1.3", "@npmcli/run-script@^4.1.7": version "4.2.1" resolved "https://registry.yarnpkg.com/@npmcli/run-script/-/run-script-4.2.1.tgz#c07c5c71bc1c70a5f2a06b0d4da976641609b946" @@ -1940,321 +2532,74 @@ dependencies: nx "15.6.3" -"@oclif/color@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@oclif/color/-/color-1.0.1.tgz#20ab9205e0924c6388918a88874e1f4b32df9970" - integrity sha512-qjYr+izgWdIVOroiBKqTzQgc1r5Wd9QB1J7yGM2EeelqhBARiiVLRZL45vhV4zdyTRdDkZS0EBzFwQap+nliLA== - dependencies: - ansi-styles "^4.2.1" - chalk "^4.1.0" - strip-ansi "^6.0.1" - supports-color "^8.1.1" - tslib "^2" - -"@oclif/core@3.27.0": - version "3.27.0" - resolved "https://registry.yarnpkg.com/@oclif/core/-/core-3.27.0.tgz#a22a4ff4e5811db7a182b1687302237a57802381" - integrity sha512-Fg93aNFvXzBq5L7ztVHFP2nYwWU1oTCq48G0TjF/qC1UN36KWa2H5Hsm72kERd5x/sjy2M2Tn4kDEorUlpXOlw== +"@oclif/core@4.0.11", "@oclif/core@^4": + version "4.0.11" + resolved "https://registry.yarnpkg.com/@oclif/core/-/core-4.0.11.tgz#0a774d00ccab8ee63eb01c5ad2bffc69a170dba8" + integrity sha512-cZLLdSm9tGSbuoRjjgXf128zvPZH+afjQMQcrvDfoN347KvPg75ne8YJ8qHix+T3Vl03iXfgIH6guQN0kLgmjg== dependencies: - "@types/cli-progress" "^3.11.5" ansi-escapes "^4.3.2" - ansi-styles "^4.3.0" - cardinal "^2.1.1" - chalk "^4.1.2" + ansis "^3.1.1" clean-stack "^3.0.1" - cli-progress "^3.12.0" - color "^4.2.3" + cli-spinners "^2.9.2" debug "^4.3.5" ejs "^3.1.10" get-package-type "^0.1.0" globby "^11.1.0" - hyperlinker "^1.0.0" indent-string "^4.0.0" is-wsl "^2.2.0" - js-yaml "^3.14.1" - minimatch "^9.0.4" - natural-orderby "^2.0.3" - object-treeify "^1.1.33" - password-prompt "^1.1.3" - slice-ansi "^4.0.0" + lilconfig "^3.1.2" + minimatch "^9.0.5" string-width "^4.2.3" - strip-ansi "^6.0.1" - supports-color "^8.1.1" - supports-hyperlinks "^2.2.0" + supports-color "^8" widest-line "^3.1.0" wordwrap "^1.0.0" wrap-ansi "^7.0.0" -"@oclif/core@^1.2.1", "@oclif/core@^1.3.6": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@oclif/core/-/core-1.6.1.tgz#8300132782fd5845e3f08026f2fbd95917e8c2dd" - integrity sha512-qB33YT3JUc7oH3Mtoov8yRETdMiQKsQNdoB2ozRq8JOCxtqI2f0XiQ6eqes3GpeMZKucl4mmonws5LYraLdQlg== - dependencies: - "@oclif/linewrap" "^1.0.0" - "@oclif/screen" "^3.0.2" - ansi-escapes "^4.3.2" - ansi-styles "^4.3.0" - cardinal "^2.1.1" - chalk "^4.1.2" - clean-stack "^3.0.1" - cli-progress "^3.10.0" - debug "^4.3.3" - ejs "^3.1.6" - fs-extra "^9.1.0" - get-package-type "^0.1.0" - globby "^11.1.0" - hyperlinker "^1.0.0" - indent-string "^4.0.0" - is-wsl "^2.2.0" - js-yaml "^3.14.1" - lodash "^4.17.21" - natural-orderby "^2.0.3" - object-treeify "^1.1.33" - password-prompt "^1.1.2" - semver "^7.3.5" - string-width "^4.2.3" - strip-ansi "^6.0.1" - supports-color "^8.1.1" - supports-hyperlinks "^2.2.0" - tslib "^2.3.1" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" - -"@oclif/core@^1.23.1": - version "1.26.2" - resolved "https://registry.yarnpkg.com/@oclif/core/-/core-1.26.2.tgz#763c68dc91388225acd6f0819c90f93e5d8cde41" - integrity sha512-6jYuZgXvHfOIc9GIaS4T3CIKGTjPmfAxuMcbCbMRKJJl4aq/4xeRlEz0E8/hz8HxvxZBGvN2GwAUHlrGWQVrVw== - dependencies: - "@oclif/linewrap" "^1.0.0" - "@oclif/screen" "^3.0.4" - ansi-escapes "^4.3.2" - ansi-styles "^4.3.0" - cardinal "^2.1.1" - chalk "^4.1.2" - clean-stack "^3.0.1" - cli-progress "^3.10.0" - debug "^4.3.4" - ejs "^3.1.6" - fs-extra "^9.1.0" - get-package-type "^0.1.0" - globby "^11.1.0" - hyperlinker "^1.0.0" - indent-string "^4.0.0" - is-wsl "^2.2.0" - js-yaml "^3.14.1" - natural-orderby "^2.0.3" - object-treeify "^1.1.33" - password-prompt "^1.1.2" - semver "^7.3.7" - string-width "^4.2.3" - strip-ansi "^6.0.1" - supports-color "^8.1.1" - supports-hyperlinks "^2.2.0" - tslib "^2.4.1" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" - -"@oclif/core@^1.3.1": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@oclif/core/-/core-1.6.3.tgz#3d1dd4e033f5512ac35963a73878257142390838" - integrity sha512-a3DrPNlOYemwnzxuJ3tINjqpMVIYe56Mg+XaQo0nGsqGSk69wF5Q/hD8plsWrtwdkeIxwxhgl7T699EJypAUwg== - dependencies: - "@oclif/linewrap" "^1.0.0" - "@oclif/screen" "^3.0.2" - ansi-escapes "^4.3.2" - ansi-styles "^4.3.0" - cardinal "^2.1.1" - chalk "^4.1.2" - clean-stack "^3.0.1" - cli-progress "^3.10.0" - debug "^4.3.3" - ejs "^3.1.6" - fs-extra "^9.1.0" - get-package-type "^0.1.0" - globby "^11.1.0" - hyperlinker "^1.0.0" - indent-string "^4.0.0" - is-wsl "^2.2.0" - js-yaml "^3.14.1" - lodash "^4.17.21" - natural-orderby "^2.0.3" - object-treeify "^1.1.33" - password-prompt "^1.1.2" - semver "^7.3.5" - string-width "^4.2.3" - strip-ansi "^6.0.1" - supports-color "^8.1.1" - supports-hyperlinks "^2.2.0" - tslib "^2.3.1" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" - -"@oclif/core@^2.15.0", "@oclif/core@^2.7.1": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@oclif/core/-/core-2.16.0.tgz#e6f3c6c359d4313a15403d8652bbdd0e99ce4b3a" - integrity sha512-dL6atBH0zCZl1A1IXCKJgLPrM/wR7K+Wi401E/IvqsK8m2iCHW+0TEOGrans/cuN3oTW+uxIyJFHJ8Im0k4qBw== - dependencies: - "@types/cli-progress" "^3.11.0" - ansi-escapes "^4.3.2" - ansi-styles "^4.3.0" - cardinal "^2.1.1" - chalk "^4.1.2" - clean-stack "^3.0.1" - cli-progress "^3.12.0" - debug "^4.3.4" - ejs "^3.1.8" - get-package-type "^0.1.0" - globby "^11.1.0" - hyperlinker "^1.0.0" - indent-string "^4.0.0" - is-wsl "^2.2.0" - js-yaml "^3.14.1" - natural-orderby "^2.0.3" - object-treeify "^1.1.33" - password-prompt "^1.1.2" - slice-ansi "^4.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - supports-color "^8.1.1" - supports-hyperlinks "^2.2.0" - ts-node "^10.9.1" - tslib "^2.5.0" - widest-line "^3.1.0" - wordwrap "^1.0.0" - wrap-ansi "^7.0.0" - -"@oclif/core@^2.8.7": - version "2.8.10" - resolved "https://registry.yarnpkg.com/@oclif/core/-/core-2.8.10.tgz#01d2ce3ad9f9f8679e3d4c506f528e9de7de74a5" - integrity sha512-coRn9vYDEnoE8Vg20aavts9+Bt5QrHhbdh0cDkImopV0MgT8i/VmgL04D33+qoHQH20XzBOMqrjk+bqQzqyaHg== - dependencies: - "@types/cli-progress" "^3.11.0" - ansi-escapes "^4.3.2" - ansi-styles "^4.3.0" - cardinal "^2.1.1" - chalk "^4.1.2" - clean-stack "^3.0.1" - cli-progress "^3.12.0" - debug "^4.3.4" - ejs "^3.1.8" - fs-extra "^9.1.0" - get-package-type "^0.1.0" - globby "^11.1.0" - hyperlinker "^1.0.0" - indent-string "^4.0.0" - is-wsl "^2.2.0" - js-yaml "^3.14.1" - natural-orderby "^2.0.3" - object-treeify "^1.1.33" - password-prompt "^1.1.2" - semver "^7.3.7" - string-width "^4.2.3" - strip-ansi "^6.0.1" - supports-color "^8.1.1" - supports-hyperlinks "^2.2.0" - ts-node "^10.9.1" - tslib "^2.5.0" - widest-line "^3.1.0" - wordwrap "^1.0.0" - wrap-ansi "^7.0.0" - -"@oclif/linewrap@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@oclif/linewrap/-/linewrap-1.0.0.tgz#aedcb64b479d4db7be24196384897b5000901d91" - integrity sha512-Ups2dShK52xXa8w6iBWLgcjPJWjais6KPJQq3gQ/88AY6BXoTX+MIGFPrWQO1KLMiQfoTpcLnUwloN4brrVUHw== - -"@oclif/plugin-autocomplete@1.3.10": - version "1.3.10" - resolved "https://registry.yarnpkg.com/@oclif/plugin-autocomplete/-/plugin-autocomplete-1.3.10.tgz#3b6ff23ca03513f05b6719ddf51f01b35bd8bf69" - integrity sha512-oQl7ZqXhXJUOH26mDPcqcMGmcdIoK/uQPSpUBrfLa1iaQ30slTs0T7KOzg+vwKuPqIIF1nTCPuH67lE8GvUPTw== - dependencies: - "@oclif/core" "^1.23.1" - chalk "^4.1.0" - debug "^4.3.4" - fs-extra "^9.0.1" - -"@oclif/plugin-help@5.1.12": - version "5.1.12" - resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-5.1.12.tgz#24a18631eb9b22cb55e1a3b8e4f6039fd42727e6" - integrity sha512-HvH/RubJxqCinP0vUWQLTOboT+SfjfL8h40s+PymkWaldIcXlpoRaJX50vz+SjZIs7uewZwEk8fzLqpF/BWXlg== +"@oclif/plugin-autocomplete@3.1.6": + version "3.1.6" + resolved "https://registry.yarnpkg.com/@oclif/plugin-autocomplete/-/plugin-autocomplete-3.1.6.tgz#627ab2b0d9afa95b76f4dc0ba68c0980ee877740" + integrity sha512-Eo13RHSr7c5I5miatEBGhKVkLEADzN8taUlYOs5vbRWtWlR/FoDnwSZJ72gBvvayvCHEqlBOaNBn/wufxdrDAg== dependencies: - "@oclif/core" "^1.3.6" + "@oclif/core" "^4" + ansis "^3.2.0" + debug "^4.3.5" + ejs "^3.1.10" -"@oclif/plugin-help@^5.1.19": - version "5.2.20" - resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-5.2.20.tgz#4035a0ac231f95fb8e334da342175e3ca00f6abc" - integrity sha512-u+GXX/KAGL9S10LxAwNUaWdzbEBARJ92ogmM7g3gDVud2HioCmvWQCDohNRVZ9GYV9oKwZ/M8xwd6a1d95rEKQ== +"@oclif/plugin-help@6.2.5", "@oclif/plugin-help@^6.2.2": + version "6.2.5" + resolved "https://registry.yarnpkg.com/@oclif/plugin-help/-/plugin-help-6.2.5.tgz#a1d8e8469b3447c055ca3ab5444388a822d06517" + integrity sha512-/NgP6j5THCWDxQj3Mba+IIidf8fBtOT5Wh6ygb2WdWLSxcsRXSQUiJKKOXu8e/N5+KQeuG2Yko2hFxd2cZUzMQ== dependencies: - "@oclif/core" "^2.15.0" + "@oclif/core" "^4" -"@oclif/plugin-not-found@2.3.1": - version "2.3.1" - resolved "https://registry.yarnpkg.com/@oclif/plugin-not-found/-/plugin-not-found-2.3.1.tgz#8fe1019fdeeb77be055314662bb9180808222e80" - integrity sha512-AeNBw+zSkRpePmpXO8xlL072VF2/R2yK3qsVs/JF26Yw1w77TWuRTdFR+hFotJtFCJ4QYqhNtKSjdryCO9AXsA== +"@oclif/plugin-not-found@3.2.10", "@oclif/plugin-not-found@^3.2.3": + version "3.2.10" + resolved "https://registry.yarnpkg.com/@oclif/plugin-not-found/-/plugin-not-found-3.2.10.tgz#2d685d784ebc46a21f5b2d0c264ca44bb0f97547" + integrity sha512-Bevp3hcv1IhNgljugIhxL5ARcwxsQmiR9yGOozURuZBX3IjsHBPhI2I92wKA2KM5zRgh4zOm6gvoP8gcHlhLJA== dependencies: - "@oclif/color" "^1.0.0" - "@oclif/core" "^1.2.1" + "@inquirer/confirm" "^3.1.14" + "@oclif/core" "^4" + ansis "^3.2.0" fast-levenshtein "^3.0.0" - lodash "^4.17.21" -"@oclif/plugin-not-found@^2.3.7": - version "2.4.3" - resolved "https://registry.yarnpkg.com/@oclif/plugin-not-found/-/plugin-not-found-2.4.3.tgz#3d24095adb0f3876cb4bcfdfdcb775086cf6d4b5" - integrity sha512-nIyaR4y692frwh7wIHZ3fb+2L6XEecQwRDIb4zbEam0TvaVmBQWZoColQyWA84ljFBPZ8XWiQyTz+ixSwdRkqg== - dependencies: - "@oclif/core" "^2.15.0" - chalk "^4" - fast-levenshtein "^3.0.0" - -"@oclif/plugin-warn-if-update-available@2.0.40": - version "2.0.40" - resolved "https://registry.yarnpkg.com/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-2.0.40.tgz#17a9bf329ceffdfe578176e9cd7cc6feebcf3034" - integrity sha512-sVYAP/FHwfeljTBtjCIKuJeNpKOFE3mAGkssXyrDu2uodUJV3xvDZ6Lkj7jBaI0eeQEzrN1rTdcbfewifZ09xA== +"@oclif/plugin-warn-if-update-available@3.1.8", "@oclif/plugin-warn-if-update-available@^3.0.19": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-3.1.8.tgz#9bef3c7d456bf359728b3d0093dec0d53f59c723" + integrity sha512-8MVaQGnvUq/mBYOVLVC1ZniwGHckRULS75s2PVT/9A4MRPzLmtNpaV8Ncra+X4cvxMJQB7KhbJN3v1itWyHnHg== dependencies: - "@oclif/core" "^2.8.7" - chalk "^4.1.0" - debug "^4.1.0" - fs-extra "^9.0.1" + "@oclif/core" "^4" + ansis "^3.2.0" + debug "^4.3.5" http-call "^5.2.2" lodash "^4.17.21" - semver "^7.5.3" - -"@oclif/plugin-warn-if-update-available@^2.0.14": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@oclif/plugin-warn-if-update-available/-/plugin-warn-if-update-available-2.1.1.tgz#e645d3d735241422d3f75e8fbf5c68db575f4c23" - integrity sha512-y7eSzT6R5bmTIJbiMMXgOlbBpcWXGlVhNeQJBLBCCy1+90Wbjyqf6uvY0i2WcO4sh/THTJ20qCW80j3XUlgDTA== - dependencies: - "@oclif/core" "^2.15.0" - chalk "^4.1.0" - debug "^4.1.0" - http-call "^5.2.2" - lodash.template "^4.5.0" - semver "^7.5.4" - -"@oclif/screen@^3.0.2": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-3.0.2.tgz#969054308fe98d130c02844a45cc792199b75670" - integrity sha512-S/SF/XYJeevwIgHFmVDAFRUvM3m+OjhvCAYMk78ZJQCYCQ5wS7j+LTt1ZEv2jpEEGg2tx/F6TYYWxddNAYHrFQ== - -"@oclif/screen@^3.0.4": - version "3.0.8" - resolved "https://registry.yarnpkg.com/@oclif/screen/-/screen-3.0.8.tgz#f746549c3ae52fdb7794dfc244dfba98ebca37f2" - integrity sha512-yx6KAqlt3TAHBduS2fMQtJDL2ufIHnDRArrJEOoTTuizxqmjLT+psGYOHpmMl3gvQpFJ11Hs76guUUktzAF9Bg== - -"@oclif/test@2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@oclif/test/-/test-2.1.0.tgz#e5a0ba619c890770782e48c82d18f5921e2d2b9f" - integrity sha512-o+JTv3k28aMUxywJUlJY1/DORLqumoZFRII492phOmtXM16rD6Luy3z1qinT4BvEtPj2BzOPd2whr/VdYszaYw== - dependencies: - "@oclif/core" "^1.3.1" - fancy-test "^2.0.0" -"@octokit/auth-token@^2.4.4": - version "2.5.0" - resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-2.5.0.tgz#27c37ea26c205f28443402477ffd261311f21e36" - integrity sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g== +"@oclif/test@4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@oclif/test/-/test-4.0.4.tgz#e7e16d9a3edf2d2b83c8066f3f08569b4aa84e4f" + integrity sha512-O0lGcUl6sq4ijgjPimbx32O6DPCoHknzrNsA+X+XzWD9DsEv0SK6Tib2+22cOqLzLItr+gU73pIYttiD5+UWag== dependencies: - "@octokit/types" "^6.0.3" + ansis "^3.2.0" + debug "^4.3.5" "@octokit/auth-token@^3.0.0": version "3.0.1" @@ -2263,19 +2608,6 @@ dependencies: "@octokit/types" "^7.0.0" -"@octokit/core@^3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b" - integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== - dependencies: - "@octokit/auth-token" "^2.4.4" - "@octokit/graphql" "^4.5.8" - "@octokit/request" "^5.6.0" - "@octokit/request-error" "^2.0.5" - "@octokit/types" "^6.0.3" - before-after-hook "^2.2.0" - universal-user-agent "^6.0.0" - "@octokit/core@^4.0.0": version "4.0.5" resolved "https://registry.yarnpkg.com/@octokit/core/-/core-4.0.5.tgz#589e68c0a35d2afdcd41dafceab072c2fbc6ab5f" @@ -2289,15 +2621,6 @@ before-after-hook "^2.2.0" universal-user-agent "^6.0.0" -"@octokit/endpoint@^6.0.1": - version "6.0.12" - resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-6.0.12.tgz#3b4d47a4b0e79b1027fb8d75d4221928b2d05658" - integrity sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA== - dependencies: - "@octokit/types" "^6.0.3" - is-plain-object "^5.0.0" - universal-user-agent "^6.0.0" - "@octokit/endpoint@^7.0.0": version "7.0.2" resolved "https://registry.yarnpkg.com/@octokit/endpoint/-/endpoint-7.0.2.tgz#11ee868406ba7bb1642e61bbe676d641f79f02be" @@ -2307,15 +2630,6 @@ is-plain-object "^5.0.0" universal-user-agent "^6.0.0" -"@octokit/graphql@^4.5.8": - version "4.8.0" - resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-4.8.0.tgz#664d9b11c0e12112cbf78e10f49a05959aa22cc3" - integrity sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg== - dependencies: - "@octokit/request" "^5.6.0" - "@octokit/types" "^6.0.3" - universal-user-agent "^6.0.0" - "@octokit/graphql@^5.0.0": version "5.0.1" resolved "https://registry.yarnpkg.com/@octokit/graphql/-/graphql-5.0.1.tgz#a06982514ad131fb6fbb9da968653b2233fade9b" @@ -2325,11 +2639,6 @@ "@octokit/types" "^7.0.0" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^11.2.0": - version "11.2.0" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" - integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== - "@octokit/openapi-types@^13.11.0": version "13.12.0" resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-13.12.0.tgz#cd49f28127ee06ee3edc6f2b5f5648c7332f6014" @@ -2340,13 +2649,6 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== -"@octokit/plugin-paginate-rest@^2.16.8": - version "2.17.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz#32e9c7cab2a374421d3d0de239102287d791bce7" - integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== - dependencies: - "@octokit/types" "^6.34.0" - "@octokit/plugin-paginate-rest@^4.0.0": version "4.3.1" resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-4.3.1.tgz#553e653ee0318605acd23bf3a799c8bfafdedae3" @@ -2359,14 +2661,6 @@ resolved "https://registry.yarnpkg.com/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== -"@octokit/plugin-rest-endpoint-methods@^5.12.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz#8c46109021a3412233f6f50d28786f8e552427ba" - integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== - dependencies: - "@octokit/types" "^6.34.0" - deprecation "^2.3.1" - "@octokit/plugin-rest-endpoint-methods@^6.0.0": version "6.6.2" resolved "https://registry.yarnpkg.com/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.6.2.tgz#cfd1c7280940d5a82d9af12566bafcb33f22bee4" @@ -2375,15 +2669,6 @@ "@octokit/types" "^7.5.0" deprecation "^2.3.1" -"@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" - integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== - dependencies: - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - once "^1.4.0" - "@octokit/request-error@^3.0.0": version "3.0.1" resolved "https://registry.yarnpkg.com/@octokit/request-error/-/request-error-3.0.1.tgz#3fd747913c06ab2195e52004a521889dadb4b295" @@ -2393,18 +2678,6 @@ deprecation "^2.0.0" once "^1.4.0" -"@octokit/request@^5.6.0": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@octokit/request/-/request-5.6.2.tgz#1aa74d5da7b9e04ac60ef232edd9a7438dcf32d8" - integrity sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA== - dependencies: - "@octokit/endpoint" "^6.0.1" - "@octokit/request-error" "^2.1.0" - "@octokit/types" "^6.16.1" - is-plain-object "^5.0.0" - node-fetch "^2.6.1" - universal-user-agent "^6.0.0" - "@octokit/request@^6.0.0": version "6.2.1" resolved "https://registry.yarnpkg.com/@octokit/request/-/request-6.2.1.tgz#3ceeb22dab09a29595d96594b6720fc14495cf4e" @@ -2417,16 +2690,6 @@ node-fetch "^2.6.7" universal-user-agent "^6.0.0" -"@octokit/rest@^18.0.6": - version "18.12.0" - resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" - integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== - dependencies: - "@octokit/core" "^3.5.1" - "@octokit/plugin-paginate-rest" "^2.16.8" - "@octokit/plugin-request-log" "^1.0.4" - "@octokit/plugin-rest-endpoint-methods" "^5.12.0" - "@octokit/rest@^19.0.3": version "19.0.4" resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-19.0.4.tgz#fd8bed1cefffa486e9ae46a9dc608ce81bcfcbdd" @@ -2437,13 +2700,6 @@ "@octokit/plugin-request-log" "^1.0.4" "@octokit/plugin-rest-endpoint-methods" "^6.0.0" -"@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0": - version "6.34.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" - integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== - dependencies: - "@octokit/openapi-types" "^11.2.0" - "@octokit/types@^7.0.0", "@octokit/types@^7.5.0": version "7.5.0" resolved "https://registry.yarnpkg.com/@octokit/types/-/types-7.5.0.tgz#85646021bd618467b7cc465d9734b3f2878c9fae" @@ -2556,17 +2812,10 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== -"@sindresorhus/is@^4.0.0": - version "4.6.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" - integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== - -"@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== - dependencies: - type-detect "4.0.8" +"@sindresorhus/is@^5.2.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-5.6.0.tgz#41dd6093d34652cddb5d5bdeee04eafc33826668" + integrity sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g== "@sinonjs/commons@^3.0.0": version "3.0.0" @@ -2582,19 +2831,499 @@ dependencies: "@sinonjs/commons" "^3.0.0" -"@sinonjs/fake-timers@^7.1.0": - version "7.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5" - integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg== +"@smithy/abort-controller@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-3.1.1.tgz#291210611ff6afecfc198d0ca72d5771d8461d16" + integrity sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader-native@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.0.tgz#f1104b30030f76f9aadcbd3cdca4377bd1ba2695" + integrity sha512-VDkpCYW+peSuM4zJip5WDfqvg2Mo/e8yxOv3VF1m11y7B8KKMKVFtmZWDe36Fvk8rGuWrPZHHXZ7rR7uM5yWyg== + dependencies: + "@smithy/util-base64" "^3.0.0" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader/-/chunked-blob-reader-3.0.0.tgz#e5d3b04e9b273ba8b7ede47461e2aa96c8aa49e0" + integrity sha512-sbnURCwjF0gSToGlsBiAmd1lRCmSn72nu9axfJu5lIx6RUEgHu6GwTMbqCdhQSi0Pumcm5vFxsi9XWXb2mTaoA== + dependencies: + tslib "^2.6.2" + +"@smithy/config-resolver@^3.0.4", "@smithy/config-resolver@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-3.0.5.tgz#727978bba7ace754c741c259486a19d3083431fd" + integrity sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/types" "^3.3.0" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + tslib "^2.6.2" + +"@smithy/core@^2.2.4": + version "2.2.6" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-2.2.6.tgz#05e3482079fff7522077e0f3ce2ee6cc507f461c" + integrity sha512-tBbVIv/ui7/lLTKayYJJvi8JLVL2SwOQTbNFEOrvzSE3ktByvsa1erwBOnAMo8N5Vu30g7lN4lLStrU75oDGuw== + dependencies: + "@smithy/middleware-endpoint" "^3.0.5" + "@smithy/middleware-retry" "^3.0.9" + "@smithy/middleware-serde" "^3.0.3" + "@smithy/protocol-http" "^4.0.3" + "@smithy/smithy-client" "^3.1.7" + "@smithy/types" "^3.3.0" + "@smithy/util-middleware" "^3.0.3" + tslib "^2.6.2" + +"@smithy/credential-provider-imds@^3.1.3", "@smithy/credential-provider-imds@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-3.1.4.tgz#797116f68cc3ffa658469558cc014f25d9febe09" + integrity sha512-NKyH01m97Xa5xf3pB2QOF3lnuE8RIK0hTVNU5zvZAwZU8uspYO4DHQVlK+Y5gwSrujTfHvbfd1D9UFJAc0iYKQ== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/property-provider" "^3.1.3" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + tslib "^2.6.2" + +"@smithy/eventstream-codec@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-3.1.2.tgz#4a1c72b34400631b829241151984a1ad8c4f963c" + integrity sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@smithy/types" "^3.3.0" + "@smithy/util-hex-encoding" "^3.0.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-browser@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.4.tgz#98d6e7ae60d297e37ee7775af2a7a8bbe574579d" + integrity sha512-Eo4anLZX6ltGJTZ5yJMc80gZPYYwBn44g0h7oFq6et+TYr5dUsTpIcDbz2evsOKIZhZ7zBoFWHtBXQ4QQeb5xA== + dependencies: + "@smithy/eventstream-serde-universal" "^3.0.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-config-resolver@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.3.tgz#f852e096d0ad112363b4685e1d441088d1fce67a" + integrity sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-node@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.4.tgz#6301752ca51b3ebabcd2dec112f1dacd990de4c1" + integrity sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg== + dependencies: + "@smithy/eventstream-serde-universal" "^3.0.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-universal@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.4.tgz#6754de5b94bdc286d8ef1d6bcf22d80f6ab68f30" + integrity sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A== + dependencies: + "@smithy/eventstream-codec" "^3.1.2" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/fetch-http-handler@^3.2.0", "@smithy/fetch-http-handler@^3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.1.tgz#04ba6804cdf2b1cb783229eede6b9cd8653c5543" + integrity sha512-0w0bgUvZmfa0vHN8a+moByhCJT07WN6AHKEhFSOLsDpnszm+5dLVv5utGaqbhOrZ/aF5x3xuPMs/oMCd+4O5xg== + dependencies: + "@smithy/protocol-http" "^4.0.3" + "@smithy/querystring-builder" "^3.0.3" + "@smithy/types" "^3.3.0" + "@smithy/util-base64" "^3.0.0" + tslib "^2.6.2" + +"@smithy/hash-blob-browser@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.2.tgz#90281c1f183d93686fb4f26107f1819644d68829" + integrity sha512-hAbfqN2UbISltakCC2TP0kx4LqXBttEv2MqSPE98gVuDFMf05lU+TpC41QtqGP3Ff5A3GwZMPfKnEy0VmEUpmg== + dependencies: + "@smithy/chunked-blob-reader" "^3.0.0" + "@smithy/chunked-blob-reader-native" "^3.0.0" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/hash-node@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-3.0.3.tgz#82c5cb7b0f1a29ee7319081853d2d158c07dff24" + integrity sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw== + dependencies: + "@smithy/types" "^3.3.0" + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/hash-stream-node@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-3.1.2.tgz#89f0290ae44b113863878e75b10c484ff48af71c" + integrity sha512-PBgDMeEdDzi6JxKwbfBtwQG9eT9cVwsf0dZzLXoJF4sHKHs5HEo/3lJWpn6jibfJwT34I1EBXpBnZE8AxAft6g== + dependencies: + "@smithy/types" "^3.3.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/invalid-dependency@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz#8d9fd70e3a94b565a4eba4ffbdc95238e1930528" + integrity sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/is-array-buffer@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" + integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== + dependencies: + tslib "^2.6.2" + +"@smithy/is-array-buffer@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz#9a95c2d46b8768946a9eec7f935feaddcffa5e7a" + integrity sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ== + dependencies: + tslib "^2.6.2" + +"@smithy/md5-js@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-3.0.3.tgz#55ee40aa24075b096c39f7910590c18ff7660c98" + integrity sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q== dependencies: - "@sinonjs/commons" "^1.7.0" + "@smithy/types" "^3.3.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" -"@szmarczak/http-timer@^4.0.5": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807" - integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w== +"@smithy/middleware-content-length@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-3.0.3.tgz#426a7f907cc3c0a5d81deb84e16d38303e5a9ad8" + integrity sha512-Dbz2bzexReYIQDWMr+gZhpwBetNXzbhnEMhYKA6urqmojO14CsXjnsoPYO8UL/xxcawn8ZsuVU61ElkLSltIUQ== + dependencies: + "@smithy/protocol-http" "^4.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/middleware-endpoint@^3.0.4", "@smithy/middleware-endpoint@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-3.0.5.tgz#76e8a559e891282d3ede9ab8e228e66cbee89b21" + integrity sha512-V4acqqrh5tDxUEGVTOgf2lYMZqPQsoGntCrjrJZEeBzEzDry2d2vcI1QCXhGltXPPY+BMc6eksZMguA9fIY8vA== + dependencies: + "@smithy/middleware-serde" "^3.0.3" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + "@smithy/url-parser" "^3.0.3" + "@smithy/util-middleware" "^3.0.3" + tslib "^2.6.2" + +"@smithy/middleware-retry@^3.0.7", "@smithy/middleware-retry@^3.0.9": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-3.0.9.tgz#3d5c33b49ad372bf02c8525931febc5cc246815c" + integrity sha512-Mrv9omExU1gA7Y0VEJG2LieGfPYtwwcEiOnVGZ54a37NEMr66TJ0glFslOJFuKWG6izg5DpKIUmDV9rRxjm47Q== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/protocol-http" "^4.0.3" + "@smithy/service-error-classification" "^3.0.3" + "@smithy/smithy-client" "^3.1.7" + "@smithy/types" "^3.3.0" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-retry" "^3.0.3" + tslib "^2.6.2" + uuid "^9.0.1" + +"@smithy/middleware-serde@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz#74d974460f74d99f38c861e6862984543a880a66" + integrity sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/middleware-stack@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz#91845c7e61e6f137fa912b623b6def719a4f6ce7" + integrity sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/node-config-provider@^3.1.3", "@smithy/node-config-provider@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz#05647bed666aa8036a1ad72323c1942e5d421be1" + integrity sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ== + dependencies: + "@smithy/property-provider" "^3.1.3" + "@smithy/shared-ini-file-loader" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/node-http-handler@^3.1.1", "@smithy/node-http-handler@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-3.1.2.tgz#2d753c07f11e7a3da3534b156320d1e0e9401617" + integrity sha512-Td3rUNI7qqtoSLTsJBtsyfoG4cF/XMFmJr6Z2dX8QNzIi6tIW6YmuyFml8mJ2cNpyWNqITKbROMOFrvQjmsOvw== + dependencies: + "@smithy/abort-controller" "^3.1.1" + "@smithy/protocol-http" "^4.0.3" + "@smithy/querystring-builder" "^3.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/property-provider@^3.1.3": + version "3.1.3" + resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-3.1.3.tgz#afd57ea82a3f6c79fbda95e3cb85c0ee0a79f39a" + integrity sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/protocol-http@^4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-4.0.3.tgz#acf16058504e3cce2dbe8abf94f7b544cd09d3f4" + integrity sha512-x5jmrCWwQlx+Zv4jAtc33ijJ+vqqYN+c/ZkrnpvEe/uDas7AT7A/4Rc2CdfxgWv4WFGmEqODIrrUToPN6DDkGw== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/querystring-builder@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz#6b0e566f885bb84938d077c69e8f8555f686af13" + integrity sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw== + dependencies: + "@smithy/types" "^3.3.0" + "@smithy/util-uri-escape" "^3.0.0" + tslib "^2.6.2" + +"@smithy/querystring-parser@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz#272a6b83f88dfcbbec8283d72a6bde850cc00091" + integrity sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/service-error-classification@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz#73484255060a094aa9372f6cd972dcaf97e3ce80" + integrity sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ== + dependencies: + "@smithy/types" "^3.3.0" + +"@smithy/shared-ini-file-loader@^3.1.3", "@smithy/shared-ini-file-loader@^3.1.4": + version "3.1.4" + resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz#7dceaf5a5307a2ee347ace8aba17312a1a3ede15" + integrity sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/signature-v4@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-3.1.2.tgz#63fc0d4f9a955e902138fb0a57fafc96b9d4e8bb" + integrity sha512-3BcPylEsYtD0esM4Hoyml/+s7WP2LFhcM3J2AGdcL2vx9O60TtfpDOL72gjb4lU8NeRPeKAwR77YNyyGvMbuEA== + dependencies: + "@smithy/is-array-buffer" "^3.0.0" + "@smithy/types" "^3.3.0" + "@smithy/util-hex-encoding" "^3.0.0" + "@smithy/util-middleware" "^3.0.3" + "@smithy/util-uri-escape" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/smithy-client@^3.1.5", "@smithy/smithy-client@^3.1.7": + version "3.1.7" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-3.1.7.tgz#56c1eee68b903053e246fb141253a567cc4d9930" + integrity sha512-nZbJZB0XI3YnaFBWGDBr7kjaew6O0oNYNmopyIz6gKZEbxzrtH7rwvU1GcVxcSFoOwWecLJEe79fxEMljHopFQ== + dependencies: + "@smithy/middleware-endpoint" "^3.0.5" + "@smithy/middleware-stack" "^3.0.3" + "@smithy/protocol-http" "^4.0.3" + "@smithy/types" "^3.3.0" + "@smithy/util-stream" "^3.0.6" + tslib "^2.6.2" + +"@smithy/types@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.3.0.tgz#fae037c733d09bc758946a01a3de0ef6e210b16b" + integrity sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA== + dependencies: + tslib "^2.6.2" + +"@smithy/url-parser@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-3.0.3.tgz#e8a060d9810b24b1870385fc2b02485b8a6c5955" + integrity sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A== + dependencies: + "@smithy/querystring-parser" "^3.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/util-base64@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-3.0.0.tgz#f7a9a82adf34e27a72d0719395713edf0e493017" + integrity sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ== + dependencies: + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-body-length-browser@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz#86ec2f6256310b4845a2f064e2f571c1ca164ded" + integrity sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ== + dependencies: + tslib "^2.6.2" + +"@smithy/util-body-length-node@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz#99a291bae40d8932166907fe981d6a1f54298a6d" + integrity sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA== + dependencies: + tslib "^2.6.2" + +"@smithy/util-buffer-from@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b" + integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== + dependencies: + "@smithy/is-array-buffer" "^2.2.0" + tslib "^2.6.2" + +"@smithy/util-buffer-from@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz#559fc1c86138a89b2edaefc1e6677780c24594e3" + integrity sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA== + dependencies: + "@smithy/is-array-buffer" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-config-provider@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz#62c6b73b22a430e84888a8f8da4b6029dd5b8efe" + integrity sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ== + dependencies: + tslib "^2.6.2" + +"@smithy/util-defaults-mode-browser@^3.0.7": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.9.tgz#a7035ca57f359810f52d828c68ad8746b0120113" + integrity sha512-WKPcElz92MAQG09miBdb0GxEH/MwD5GfE8g07WokITq5g6J1ROQfYCKC1wNnkqAGfrSywT7L0rdvvqlBplqiyA== dependencies: - defer-to-connect "^2.0.0" + "@smithy/property-provider" "^3.1.3" + "@smithy/smithy-client" "^3.1.7" + "@smithy/types" "^3.3.0" + bowser "^2.11.0" + tslib "^2.6.2" + +"@smithy/util-defaults-mode-node@^3.0.7": + version "3.0.9" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.9.tgz#d31cd62e9bcc005f92923fc7b6bc0366c3b0478a" + integrity sha512-dQLrUqFxqpf0GvEKEuFdgXcdZwz6oFm752h4d6C7lQz+RLddf761L2r7dSwGWzESMMB3wKj0jL+skRhEGlecjw== + dependencies: + "@smithy/config-resolver" "^3.0.5" + "@smithy/credential-provider-imds" "^3.1.4" + "@smithy/node-config-provider" "^3.1.4" + "@smithy/property-provider" "^3.1.3" + "@smithy/smithy-client" "^3.1.7" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/util-endpoints@^2.0.4": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz#e3a7a4d1c41250bfd2b2d890d591273a7d8934be" + integrity sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg== + dependencies: + "@smithy/node-config-provider" "^3.1.4" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/util-hex-encoding@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz#32938b33d5bf2a15796cd3f178a55b4155c535e6" + integrity sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ== + dependencies: + tslib "^2.6.2" + +"@smithy/util-middleware@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-3.0.3.tgz#07bf9602682f5a6c55bc2f0384303f85fc68c87e" + integrity sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw== + dependencies: + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/util-retry@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-3.0.3.tgz#9b2ac0dbb1c81f69812a8affa4d772bebfc0e049" + integrity sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w== + dependencies: + "@smithy/service-error-classification" "^3.0.3" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@smithy/util-stream@^3.0.5", "@smithy/util-stream@^3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-3.0.6.tgz#233624e0e024f5846cf1fdbfb2a2ab5352d724ee" + integrity sha512-w9i//7egejAIvplX821rPWWgaiY1dxsQUw0hXX7qwa/uZ9U3zplqTQ871jWadkcVB9gFDhkPWYVZf4yfFbZ0xA== + dependencies: + "@smithy/fetch-http-handler" "^3.2.1" + "@smithy/node-http-handler" "^3.1.2" + "@smithy/types" "^3.3.0" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-hex-encoding" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-uri-escape@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz#e43358a78bf45d50bb736770077f0f09195b6f54" + integrity sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg== + dependencies: + tslib "^2.6.2" + +"@smithy/util-utf8@^2.0.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" + integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== + dependencies: + "@smithy/util-buffer-from" "^2.2.0" + tslib "^2.6.2" + +"@smithy/util-utf8@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-3.0.0.tgz#1a6a823d47cbec1fd6933e5fc87df975286d9d6a" + integrity sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA== + dependencies: + "@smithy/util-buffer-from" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-waiter@^3.1.2": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-3.1.2.tgz#2d40c3312f3537feee763459a19acafab4c75cf3" + integrity sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw== + dependencies: + "@smithy/abort-controller" "^3.1.1" + "@smithy/types" "^3.3.0" + tslib "^2.6.2" + +"@szmarczak/http-timer@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-5.0.1.tgz#c7c1bf1141cdd4751b0399c8fc7b8b664cd5be3a" + integrity sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw== + dependencies: + defer-to-connect "^2.0.1" "@tootallnate/once@1": version "1.1.2" @@ -2676,32 +3405,10 @@ resolved "https://registry.yarnpkg.com/@types/buffer-json/-/buffer-json-2.0.0.tgz#6ec0ee9ba7334a378a9c1d849bccc1b079a33554" integrity sha512-nFKOrY93Tvv5Tobws+YbkGlPOJsn1nVpZah3BlSyQ4EniFm97KLvSr54tZ5xQp8mlf/XxbYwskNCYQB9EdrPlQ== -"@types/cacheable-request@^6.0.1": - version "6.0.3" - resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183" - integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw== - dependencies: - "@types/http-cache-semantics" "*" - "@types/keyv" "^3.1.4" - "@types/node" "*" - "@types/responselike" "^1.0.0" - -"@types/chai@*": - version "4.2.22" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.22.tgz#47020d7e4cf19194d43b5202f35f75bd2ad35ce7" - integrity sha512-tFfcE+DSTzWAgifkjik9AySNqIyNoYwmR+uecPwwD/XRNfvOjmC/FjCxpiUGDkDVDphPfCUecSQVFw+lN3M3kQ== - -"@types/cli-progress@^3.11.0": - version "3.11.0" - resolved "https://registry.yarnpkg.com/@types/cli-progress/-/cli-progress-3.11.0.tgz#ec79df99b26757c3d1c7170af8422e0fc95eef7e" - integrity sha512-XhXhBv1R/q2ahF3BM7qT5HLzJNlIL0wbcGyZVjqOTqAybAnsLisd7gy1UCyIqpL+5Iv6XhlSyzjLCnI2sIdbCg== - dependencies: - "@types/node" "*" - -"@types/cli-progress@^3.11.5": - version "3.11.5" - resolved "https://registry.yarnpkg.com/@types/cli-progress/-/cli-progress-3.11.5.tgz#9518c745e78557efda057e3f96a5990c717268c3" - integrity sha512-D4PbNRbviKyppS5ivBGyFO29POlySLmA2HyUFE4p5QGazAMM3CwkKWcvTl8gvElSuxRh6FPKL8XmidX873ou4g== +"@types/cli-progress@3.11.6": + version "3.11.6" + resolved "https://registry.yarnpkg.com/@types/cli-progress/-/cli-progress-3.11.6.tgz#94b334ebe4190f710e51c1bf9b4fedb681fa9e45" + integrity sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA== dependencies: "@types/node" "*" @@ -2717,11 +3424,6 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== -"@types/expect@^1.20.4": - version "1.20.4" - resolved "https://registry.yarnpkg.com/@types/expect/-/expect-1.20.4.tgz#8288e51737bf7e3ab5d7c77bfa695883745264e5" - integrity sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg== - "@types/graceful-fs@^4.1.3": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -2729,7 +3431,7 @@ dependencies: "@types/node" "*" -"@types/http-cache-semantics@*": +"@types/http-cache-semantics@^4.0.2": version "4.0.4" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== @@ -2787,13 +3489,6 @@ dependencies: "@types/node" "*" -"@types/keyv@^3.1.4": - version "3.1.4" - resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" - integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg== - dependencies: - "@types/node" "*" - "@types/leveldown@4.0.2": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/leveldown/-/leveldown-4.0.2.tgz#edb44a33668ae58656721bb1852345e6a2f2e42a" @@ -2810,11 +3505,6 @@ "@types/abstract-leveldown" "*" "@types/node" "*" -"@types/lodash@*": - version "4.14.177" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.177.tgz#f70c0d19c30fab101cad46b52be60363c43c4578" - integrity sha512-0fDwydE2clKe9MNfvXHBHF9WEahRuj+msTuQqOmAApNORFvhMYZKNGGJdCzuhheVjMps/ti0Ak/iJPACMaevvw== - "@types/lodash@4.14.170": version "4.14.170" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.170.tgz#0d67711d4bf7f4ca5147e9091b847479b87925d6" @@ -2844,6 +3534,13 @@ dependencies: "@types/node" "*" +"@types/mute-stream@^0.0.4": + version "0.0.4" + resolved "https://registry.yarnpkg.com/@types/mute-stream/-/mute-stream-0.0.4.tgz#77208e56a08767af6c5e1237be8888e2f255c478" + integrity sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow== + dependencies: + "@types/node" "*" + "@types/node-forge@1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-1.0.2.tgz#454299d3eaa846a38f8d88872a895b40a02e86dd" @@ -2861,10 +3558,12 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.63.tgz#1788fa8da838dbb5f9ea994b834278205db6ca2b" integrity sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ== -"@types/node@^15.6.1": - version "15.14.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa" - integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A== +"@types/node@^20.14.9": + version "20.14.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.10.tgz#a1a218290f1b6428682e3af044785e5874db469a" + integrity sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ== + dependencies: + undici-types "~5.26.4" "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2876,13 +3575,6 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/responselike@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" - integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== - dependencies: - "@types/node" "*" - "@types/semver@^7.3.12": version "7.3.13" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" @@ -2893,13 +3585,6 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.2.tgz#31f6eec1ed7ec23f4f05608d3a2d381df041f564" integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw== -"@types/sinon@*": - version "10.0.6" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.6.tgz#bc3faff5154e6ecb69b797d311b7cf0c1b523a1d" - integrity sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg== - dependencies: - "@sinonjs/fake-timers" "^7.1.0" - "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -2925,19 +3610,16 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f" integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== -"@types/vinyl@^2.0.4": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/vinyl/-/vinyl-2.0.6.tgz#b2d134603557a7c3d2b5d3dc23863ea2b5eb29b0" - integrity sha512-ayJ0iOCDNHnKpKTgBG6Q6JOnHTj9zFta+3j2b8Ejza0e4cvRyMn0ZoLEmbPrTHe5YYRlDYPvPWVdV4cTaRyH7g== - dependencies: - "@types/expect" "^1.20.4" - "@types/node" "*" - "@types/w3c-web-usb@^1.0.6": version "1.0.10" resolved "https://registry.yarnpkg.com/@types/w3c-web-usb/-/w3c-web-usb-1.0.10.tgz#cf89cccd2d93b6245e784c19afe0a9f5038d4528" integrity sha512-CHgUI5kTc/QLMP8hODUHhge0D4vx+9UiAwIGiT0sTy/B2XpdX1U5rJt6JSISgr6ikRT7vxV9EVAFeYZqUnl1gQ== +"@types/wrap-ansi@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" + integrity sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g== + "@types/ws@8.5.4": version "8.5.4" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" @@ -3241,13 +3923,6 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== - dependencies: - event-target-shim "^5.0.0" - abstract-leveldown@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz#08d19d4e26fb5be426f7a57004851b39e1795a2e" @@ -3344,11 +4019,6 @@ ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-escapes@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - ansi-escapes@^4.2.1, ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -3361,29 +4031,19 @@ ansi-regex@^2.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= - ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.0.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0, ansi-styles@^4.2.1, ansi-styles@^4.3.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== @@ -3395,10 +4055,10 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansicolors@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" - integrity sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk= +ansis@^3.1.1, ansis@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ansis/-/ansis-3.2.0.tgz#0e050c5be94784f32ffdac4b84fccba064aeae4b" + integrity sha512-Yk3BkHH9U7oPyCN3gL5Tc7CpahG/+UFv/6UG03C311Vy9lzRmA5uoxDTpU9CO3rGHL6KzJz/pdDeXZCZ5Mu/Sg== anymatch@^3.0.3: version "3.1.2" @@ -3519,10 +4179,12 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +async-retry@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280" + integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw== + dependencies: + retry "0.13.1" async@^3.2.3: version "3.2.4" @@ -3539,29 +4201,6 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -available-typed-arrays@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" - integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== - dependencies: - possible-typed-array-names "^1.0.0" - -aws-sdk@^2.1231.0: - version "2.1637.0" - resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1637.0.tgz#bcc5d7876d36b04b8f2663c101fa1eec22685c3c" - integrity sha512-oV5I/d9Bd9ktXPsHOOKaJy5R5ynN3XtxCzYn30xjTnkWmZx1QKD1BGmBGJR/DigdeNWvnuGWOSPh4KGkp6Jgag== - dependencies: - buffer "4.9.2" - events "1.1.1" - ieee754 "1.1.13" - jmespath "0.16.0" - querystring "0.2.0" - sax "1.2.1" - url "0.10.3" - util "^0.12.4" - uuid "8.0.0" - xml2js "0.6.2" - axios@1.7.2, axios@^1.0.0: version "1.7.2" resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621" @@ -3636,7 +4275,7 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.0.2, base64-js@^1.3.1: +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -3663,11 +4302,6 @@ bin-links@^3.0.0: rimraf "^3.0.0" write-file-atomic "^4.0.0" -binaryextensions@^4.15.0, binaryextensions@^4.16.0: - version "4.18.0" - resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-4.18.0.tgz#22aeada2d14de062c60e8ca59a504a5636a76ceb" - integrity sha512-PQu3Kyv9dM4FnwB7XGj1+HucW+ShvJzJqjuw1JkKVs1mWdwOKVcRjOi+pV9X52A0tNvrPCsPkbFFQb+wE1EAXw== - bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" @@ -3701,6 +4335,11 @@ bn.js@^5.2.1: resolved "https://registry.npmmirror.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== +bowser@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" + integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3766,15 +4405,6 @@ buffer-map@0.0.7: resolved "https://registry.yarnpkg.com/buffer-map/-/buffer-map-0.0.7.tgz#5c2db65f7b3a723a2d9dff8e896fada3d2dc1c5d" integrity sha512-95try3p/vMRkIAAnJDaGkFhGpT/65NoeW6XelEPjAomWYR58RQtW4khn0SwKj34kZoE7uxL7w2koZSwbnszvQQ== -buffer@4.9.2: - version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - buffer@6.0.3, buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -3813,7 +4443,7 @@ byte-size@^7.0.0: resolved "https://registry.yarnpkg.com/byte-size/-/byte-size-7.0.1.tgz#b1daf3386de7ab9d706b941a748dbfc71130dee3" integrity sha512-crQdqyCwhokxwV1UyDzLZanhkugAgft7vt0qbbdt60C6Zf3CAiGmtUCylbtYwrU6loOUw3euGrNtW1J651ot1A== -cacache@^15.0.3, cacache@^15.0.5, cacache@^15.2.0: +cacache@^15.2.0: version "15.3.0" resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== @@ -3885,23 +4515,23 @@ cacache@^16.0.6, cacache@^16.1.0: tar "^6.1.11" unique-filename "^2.0.0" -cacheable-lookup@^5.0.3: - version "5.0.4" - resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" - integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA== - -cacheable-request@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817" - integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^4.0.0" - lowercase-keys "^2.0.0" - normalize-url "^6.0.1" - responselike "^2.0.0" +cacheable-lookup@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" + integrity sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w== + +cacheable-request@^10.2.8: + version "10.2.14" + resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-10.2.14.tgz#eb915b665fda41b79652782df3f553449c406b9d" + integrity sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ== + dependencies: + "@types/http-cache-semantics" "^4.0.2" + get-stream "^6.0.1" + http-cache-semantics "^4.1.1" + keyv "^4.5.3" + mimic-response "^4.0.0" + normalize-url "^8.0.0" + responselike "^3.0.0" call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" @@ -3911,22 +4541,19 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" -call-bind@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + camelcase-keys@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" @@ -3951,13 +4578,14 @@ caniuse-lite@^1.0.30001541: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz#752f21f56f96f1b1a52e97aae98c57c562d5d9da" integrity sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw== -cardinal@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/cardinal/-/cardinal-2.1.1.tgz#7cc1055d822d212954d07b085dea251cc7bc5505" - integrity sha1-fMEFXYItISlU0HsIXeolHMe8VQU= +capital-case@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" + integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A== dependencies: - ansicolors "~0.3.2" - redeyed "~2.1.0" + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" catering@^2.0.0, catering@^2.1.0: version "2.1.1" @@ -3976,7 +4604,7 @@ chai@4.2.0: pathval "^1.1.0" type-detect "^4.0.5" -chalk@4.1.2, chalk@^4, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: +chalk@4.1.2, chalk@^4, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3984,17 +4612,6 @@ chalk@4.1.2, chalk@^4, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, c ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -4004,6 +4621,24 @@ chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +change-case@^4: + version "4.1.2" + resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12" + integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A== + dependencies: + camel-case "^4.1.2" + capital-case "^1.0.4" + constant-case "^3.0.4" + dot-case "^3.0.4" + header-case "^2.0.4" + no-case "^3.0.4" + param-case "^3.0.4" + pascal-case "^3.1.2" + path-case "^3.0.4" + sentence-case "^3.0.4" + snake-case "^3.0.4" + tslib "^2.0.3" + char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" @@ -4056,11 +4691,6 @@ clean-stack@^3.0.1: dependencies: escape-string-regexp "4.0.0" -cli-boxes@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" - integrity sha1-T6kXw+WclKAEzWH47lCdplFocUM= - cli-cursor@3.1.0, cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -4068,37 +4698,33 @@ cli-cursor@3.1.0, cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-progress@3.12.0, cli-progress@^3.12.0: +cli-progress@3.12.0: version "3.12.0" resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.12.0.tgz#807ee14b66bcc086258e444ad0f19e7d42577942" integrity sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A== dependencies: string-width "^4.2.3" -cli-progress@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/cli-progress/-/cli-progress-3.10.0.tgz#63fd9d6343c598c93542fdfa3563a8b59887d78a" - integrity sha512-kLORQrhYCAtUPLZxqsAt2YJGOvRdt34+O6jl5cQGb7iF3dM55FQZlTR+rQyIK9JUcO9bBMwZsTlND+3dmFU2Cw== - dependencies: - string-width "^4.2.0" - cli-spinners@2.6.1, cli-spinners@^2.5.0: version "2.6.1" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== -cli-table@^0.3.1: - version "0.3.6" - resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.6.tgz#e9d6aa859c7fe636981fd3787378c2a20bce92fc" - integrity sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ== - dependencies: - colors "1.0.3" +cli-spinners@^2.9.2: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== +cli-width@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.1.0.tgz#42daac41d3c254ef38ad8ac037672130173691c5" + integrity sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ== + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -4117,11 +4743,6 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= - clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -4131,36 +4752,10 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" -clone-response@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" - integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== - dependencies: - mimic-response "^1.0.0" - -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= - clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= - -clone@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - -cloneable-readable@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" - integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== - dependencies: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= cmd-shim@^4.0.1: version "4.1.0" @@ -4221,42 +4816,21 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@^1.0.0, color-name@~1.1.4: +color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-string@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - color-support@^1.1.2, color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -color@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" - integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== - dependencies: - color-convert "^2.0.1" - color-string "^1.9.0" - colors@*, colors@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -colors@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" - integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= - columnify@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.6.0.tgz#6989531713c9008bb29735e61e37acf5bd553cf3" @@ -4272,21 +4846,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.1.0.tgz#f2eaecf131f10e36e07d894698226e36ae0eb5ff" - integrity sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg== - common-ancestor-path@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7" integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - compare-func@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" @@ -4310,21 +4874,6 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" -concurrently@^7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-7.6.0.tgz#531a6f5f30cf616f355a4afb8f8fcb2bba65a49a" - integrity sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw== - dependencies: - chalk "^4.1.0" - date-fns "^2.29.1" - lodash "^4.17.21" - rxjs "^7.0.0" - shell-quote "^1.7.3" - spawn-command "^0.0.2-1" - supports-color "^8.1.0" - tree-kill "^1.2.2" - yargs "^17.3.1" - config-chain@^1.1.12: version "1.1.13" resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" @@ -4343,6 +4892,15 @@ console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control- resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +constant-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1" + integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case "^2.0.2" + content-type@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" @@ -4483,18 +5041,7 @@ cross-env@7.0.3: dependencies: cross-spawn "^7.0.1" -cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4513,23 +5060,11 @@ date-fns@2.16.1: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.16.1.tgz#05775792c3f3331da812af253e1a935851d3834b" integrity sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ== -date-fns@^2.29.1: - version "2.30.0" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" - integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== - dependencies: - "@babel/runtime" "^7.21.0" - dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -dateformat@^4.5.0: - version "4.6.3" - resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" - integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== - debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.3.2" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" @@ -4641,7 +5176,7 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -defer-to-connect@^2.0.0: +defer-to-connect@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== @@ -4654,15 +5189,6 @@ deferred-leveldown@~5.3.0: abstract-leveldown "~6.2.1" inherits "^2.0.3" -define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" @@ -4705,6 +5231,11 @@ detect-indent@^6.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== +detect-indent@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-7.0.1.tgz#cbb060a12842b9c4d333f1cac4aa4da1bb66bc25" + integrity sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g== + detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -4720,6 +5251,11 @@ detect-newline@^3.0.0: resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== +detect-newline@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-4.0.1.tgz#fcefdb5713e1fb8cb2839b8b6ee22e6716ab8f23" + integrity sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog== + dezalgo@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" @@ -4738,11 +5274,6 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diff@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -4764,6 +5295,14 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + dot-prop@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -4788,7 +5327,7 @@ duplexer@^0.1.1, duplexer@~0.1.1: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== -ejs@^3.1.10, ejs@^3.1.6, ejs@^3.1.7, ejs@^3.1.8: +ejs@^3.1.10, ejs@^3.1.7: version "3.1.8" resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b" integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ== @@ -4860,11 +5399,6 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -error@^10.4.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/error/-/error-10.4.0.tgz#6fcf0fd64bceb1e750f8ed9a3dd880f00e46a487" - integrity sha512-YxIFEJuhgcICugOUvRx5th0UM+ActZ9sjY0QJmeVwsQdvosZ7kYzc9QqS0Da3R5iUmgU5meGIxh0xBeZpMVeLw== - es-abstract@^1.19.0, es-abstract@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3" @@ -4891,18 +5425,6 @@ es-abstract@^1.19.0, es-abstract@^1.19.1: string.prototype.trimstart "^1.0.4" unbox-primitive "^1.0.1" -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -4922,7 +5444,7 @@ escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: +escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -5099,7 +5621,7 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@^4.0.0, esprima@~4.0.0: +esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -5153,41 +5675,16 @@ event-stream@=3.3.4: stream-combiner "~0.0.4" through "~2.3.1" -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= - events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -execa@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a" - integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -5233,20 +5730,6 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" -fancy-test@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fancy-test/-/fancy-test-2.0.0.tgz#f1477ae4190820318816914aabe273c0a0dbd597" - integrity sha512-SFb2D/VX9SV+wNYXO1IIh1wyxUC1GS0mYCFJOWD1ia7MPj9yE2G8jaPkw4t/pg0Sj7/YJP56OzMY4nAuJSOugQ== - dependencies: - "@types/chai" "*" - "@types/lodash" "*" - "@types/node" "*" - "@types/sinon" "*" - lodash "^4.17.13" - mock-stdin "^1.0.0" - nock "^13.0.0" - stdout-stderr "^0.1.9" - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -5279,6 +5762,17 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" +fast-glob@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -5303,6 +5797,13 @@ fast-url-parser@^1.1.3: dependencies: punycode "^1.3.2" +fast-xml-parser@4.2.5: + version "4.2.5" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz#a6747a09296a6cb34f2ae634019bf1738f3b421f" + integrity sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g== + dependencies: + strnum "^1.0.5" + fastest-levenshtein@^1.0.7: version "1.0.12" resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" @@ -5385,14 +5886,6 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" -find-yarn-workspace-root2@1.2.16: - version "1.2.16" - resolved "https://registry.yarnpkg.com/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz#60287009dd2f324f59646bdb4b7610a6b301c2a9" - integrity sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA== - dependencies: - micromatch "^4.0.2" - pkg-dir "^4.2.0" - find-yarn-workspace-root@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd" @@ -5400,13 +5893,6 @@ find-yarn-workspace-root@^2.0.0: dependencies: micromatch "^4.0.2" -first-chunk-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70" - integrity sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA= - dependencies: - readable-stream "^2.0.2" - flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -5435,12 +5921,10 @@ follow-redirects@^1.15.6: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" +form-data-encoder@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" + integrity sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw== form-data@^4.0.0: version "4.0.0" @@ -5479,7 +5963,7 @@ fs-extra@^8.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.1, fs-extra@^9.1.0: +fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== @@ -5516,11 +6000,6 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - gauge@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" @@ -5603,17 +6082,6 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.1" -get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -5634,19 +6102,12 @@ get-port@^5.1.1: resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= - -get-stream@^5.0.0, get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" +get-stdin@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575" + integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA== -get-stream@^6.0.0: +get-stream@^6.0.0, get-stream@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== @@ -5659,6 +6120,11 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +git-hooks-list@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-3.1.0.tgz#386dc531dcc17474cf094743ff30987a3d3e70fc" + integrity sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA== + git-raw-commits@^2.0.8: version "2.0.10" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.10.tgz#e2255ed9563b1c9c3ea6bd05806410290297bbc1" @@ -5713,17 +6179,10 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= -github-slugger@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.5.0.tgz#17891bbc73232051474d68bd867a34625c955f7d" - integrity sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw== - -github-username@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/github-username/-/github-username-6.0.0.tgz#d543eced7295102996cd8e4e19050ebdcbe60658" - integrity sha512-7TTrRjxblSI5l6adk9zd+cV5d6i1OrJSo3Vr9xdGqFLBQo0mz5P9eIfKCDJ7eekVGGFLbce0qbPSnktXV2BjDQ== - dependencies: - "@octokit/rest" "^18.0.6" +github-slugger@^2: + version "2.0.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-2.0.0.tgz#52cf2f9279a21eb6c59dd385b410f0c0adda8f1a" + integrity sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw== glob-parent@^5.1.1, glob-parent@^5.1.2: version "5.1.2" @@ -5751,7 +6210,7 @@ glob@7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: +glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -5786,7 +6245,7 @@ globals@^13.19.0: dependencies: type-fest "^0.20.2" -globby@^11.0.1, globby@^11.0.2, globby@^11.0.3: +globby@^11.0.2: version "11.0.4" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== @@ -5810,40 +6269,39 @@ globby@^11.0.4, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - -got@^11: - version "11.8.6" - resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" - integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== - dependencies: - "@sindresorhus/is" "^4.0.0" - "@szmarczak/http-timer" "^4.0.5" - "@types/cacheable-request" "^6.0.1" - "@types/responselike" "^1.0.0" - cacheable-lookup "^5.0.3" - cacheable-request "^7.0.2" +globby@^13.1.2: + version "13.2.2" + resolved "https://registry.yarnpkg.com/globby/-/globby-13.2.2.tgz#63b90b1bf68619c2135475cbd4e71e66aa090592" + integrity sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w== + dependencies: + dir-glob "^3.0.1" + fast-glob "^3.3.0" + ignore "^5.2.4" + merge2 "^1.4.1" + slash "^4.0.0" + +got@^13: + version "13.0.0" + resolved "https://registry.yarnpkg.com/got/-/got-13.0.0.tgz#a2402862cef27a5d0d1b07c0fb25d12b58175422" + integrity sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA== + dependencies: + "@sindresorhus/is" "^5.2.0" + "@szmarczak/http-timer" "^5.0.1" + cacheable-lookup "^7.0.0" + cacheable-request "^10.2.8" decompress-response "^6.0.0" - http2-wrapper "^1.0.0-beta.5.2" - lowercase-keys "^2.0.0" - p-cancelable "^2.0.0" - responselike "^2.0.0" + form-data-encoder "^2.1.2" + get-stream "^6.0.1" + http2-wrapper "^2.1.10" + lowercase-keys "^3.0.0" + p-cancelable "^3.0.0" + responselike "^3.0.0" graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.6: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== -graceful-fs@^4.1.5: - version "4.2.9" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" - integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== - graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -5854,11 +6312,6 @@ graphemer@^1.4.0: resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== -grouped-queue@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/grouped-queue/-/grouped-queue-2.0.0.tgz#a2c6713f2171e45db2c300a3a9d7c119d694dac8" - integrity sha512-/PiFUa7WIsl48dUeCvhIHnwNmAAzlI/eHoJl0vu3nsFA366JleY7Ff8EVTplZu5kO0MIdZjKTTnzItL61ahbnw== - growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -5881,13 +6334,6 @@ hard-rejection@^2.1.0: resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - has-bigints@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" @@ -5903,28 +6349,11 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - -has-proto@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" - integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== - has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== -has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - has-tostringtag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" @@ -5932,13 +6361,6 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-tostringtag@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - has-unicode@^2.0.0, has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -5951,12 +6373,13 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hasown@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" - integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== +header-case@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" + integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q== dependencies: - function-bind "^1.1.2" + capital-case "^1.0.4" + tslib "^2.0.3" hosted-git-info@^2.1.4: version "2.8.9" @@ -5984,12 +6407,19 @@ hosted-git-info@^5.0.0: dependencies: lru-cache "^7.5.1" +hosted-git-info@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17" + integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w== + dependencies: + lru-cache "^10.0.1" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== -http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: +http-cache-semantics@^4.1.0, http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity "sha1-q+AvyymFRgvwMjvmZENuw0dqbVo= sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" @@ -6024,13 +6454,13 @@ http-proxy-agent@^5.0.0: agent-base "6" debug "4" -http2-wrapper@^1.0.0-beta.5.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" - integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg== +http2-wrapper@^2.1.10: + version "2.2.1" + resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-2.2.1.tgz#310968153dcdedb160d8b72114363ef5fce1f64a" + integrity sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ== dependencies: quick-lru "^5.1.1" - resolve-alpn "^1.0.0" + resolve-alpn "^1.2.0" https-proxy-agent@^5.0.0: version "5.0.0" @@ -6040,11 +6470,6 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -human-signals@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" - integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== - human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" @@ -6057,11 +6482,6 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -hyperlinker@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" - integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ== - iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -6076,12 +6496,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@1.1.13: - version "1.1.13" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" - integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== - -ieee754@^1.1.13, ieee754@^1.1.4, ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -6093,13 +6508,6 @@ ignore-walk@3.0.4: dependencies: minimatch "^3.0.4" -ignore-walk@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-4.0.1.tgz#fc840e8346cf88a3a9380c5b17933cd8f4d39fa3" - integrity sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw== - dependencies: - minimatch "^3.0.4" - ignore-walk@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776" @@ -6166,7 +6574,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -6189,7 +6597,7 @@ init-package-json@^3.0.2: validate-npm-package-license "^3.0.4" validate-npm-package-name "^4.0.0" -inquirer@8.2.5, inquirer@^8.0.0, inquirer@^8.2.4: +inquirer@8.2.5, inquirer@^8.2.4: version "8.2.5" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== @@ -6219,11 +6627,6 @@ internal-slot@^1.0.3: has "^1.0.3" side-channel "^1.0.4" -interpret@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" - integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== - ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -6234,24 +6637,11 @@ ip@^2.0.0: resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== -is-arguments@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -6272,11 +6662,6 @@ is-buffer@^2.0.5: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== -is-callable@^1.1.3: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - is-callable@^1.1.4, is-callable@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" @@ -6327,11 +6712,6 @@ is-fullwidth-code-point@^1.0.0: dependencies: number-is-nan "^1.0.0" -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -6342,13 +6722,6 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -6403,6 +6776,11 @@ is-plain-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-plain-obj@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" + integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== + is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -6428,13 +6806,6 @@ is-retry-allowed@^1.1.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== -is-scoped@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-scoped/-/is-scoped-2.1.0.tgz#fef0713772658bdf5bee418608267ddae6d3566d" - integrity sha512-Cv4OpPTHAK9kHYzkzCrof3VJh7H/PrG2MBUMvvJebaaUMbqhm0YAtXnvh0I3Hnj2tMZWwrRROWLSgfJrKqWmlQ== - dependencies: - scoped-regex "^2.0.0" - is-shared-array-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6" @@ -6473,13 +6844,6 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" -is-typed-array@^1.1.3: - version "1.1.13" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" - integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== - dependencies: - which-typed-array "^1.1.14" - is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -6490,11 +6854,6 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-utf8@^0.2.0, is-utf8@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= - is-weakref@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2" @@ -6509,21 +6868,11 @@ is-wsl@^2.2.0: dependencies: is-docker "^2.0.0" -isarray@^1.0.0, isarray@~1.0.0: +isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= -isbinaryfile@^4.0.10: - version "4.0.10" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" - integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== - -isbinaryfile@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.8.tgz#5d34b94865bd4946633ecc78a026fc76c5b11fcf" - integrity sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -7050,17 +7399,12 @@ jest@29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" -jmespath@0.16.0: - version "0.16.0" - resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" - integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== - js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@3.14.1, js-yaml@^3.10.0, js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.14.1: +js-yaml@3.14.1, js-yaml@^3.10.0, js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -7180,7 +7524,7 @@ keccak@3.0.4: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -keyv@^4.0.0: +keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -7322,6 +7666,11 @@ libnpmpublish@^6.0.4: semver "^7.3.7" ssri "^9.0.0" +lilconfig@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.2.tgz#e4a7c3cb549e3a606c8dcc32e5ae1005e62c05cb" + integrity sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow== + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" @@ -7352,16 +7701,6 @@ load-json-file@^6.2.0: strip-bom "^4.0.0" type-fest "^0.6.0" -load-yaml-file@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/load-yaml-file/-/load-yaml-file-0.2.0.tgz#af854edaf2bea89346c07549122753c07372f64d" - integrity sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw== - dependencies: - graceful-fs "^4.1.5" - js-yaml "^3.13.0" - pify "^4.0.1" - strip-bom "^3.0.0" - locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -7389,11 +7728,6 @@ lodash-es@^4.17.11: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA== - lodash.escaperegexp@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" @@ -7439,32 +7773,12 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.set@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23" - integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM= - -lodash.template@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - -lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.15, lodash@^4.17.21: +lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@^4.0.0, log-symbols@^4.1.0: +log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -7472,10 +7786,22 @@ log-symbols@^4.0.0, log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lowercase-keys@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-3.0.0.tgz#c5e7d442e37ead247ae9db117a9d0a467c89d4f2" + integrity sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ== + +lru-cache@^10.0.1: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== lru-cache@^5.1.1: version "5.1.1" @@ -7521,28 +7847,6 @@ make-error@1.x, make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^10.0.1: - version "10.0.6" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.0.6.tgz#671269de09cc51208413460898efb7b36adf5534" - integrity sha512-4Gfh6lV3TLXmj7qz79hBFuvVqjYSMW6v2+sxtdX4LFQU0rK3V/txRjE0DoZb7X0IF3t9f8NO3CxPSWlvdckhVA== - dependencies: - agentkeepalive "^4.2.1" - cacache "^16.0.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" - is-lambda "^1.0.1" - lru-cache "^7.5.1" - minipass "^3.1.6" - minipass-collect "^1.0.2" - minipass-fetch "^2.0.3" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.4" - negotiator "^0.6.3" - promise-retry "^2.0.1" - socks-proxy-agent "^6.1.1" - ssri "^8.0.1" - make-fetch-happen@^10.0.3, make-fetch-happen@^10.0.6: version "10.2.1" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" @@ -7609,32 +7913,6 @@ map-stream@~0.1.0: resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= -"mem-fs-editor@^8.1.2 || ^9.0.0": - version "9.4.0" - resolved "https://registry.yarnpkg.com/mem-fs-editor/-/mem-fs-editor-9.4.0.tgz#0cc1cf61350e33c25fc364c97fb0551eb32b8c9b" - integrity sha512-HSSOLSVRrsDdui9I6i96dDtG+oAez/4EB2g4cjSrNhgNQ3M+L57/+22NuPdORSoxvOHjIg/xeOE+C0wwF91D2g== - dependencies: - binaryextensions "^4.16.0" - commondir "^1.0.1" - deep-extend "^0.6.0" - ejs "^3.1.6" - globby "^11.0.3" - isbinaryfile "^4.0.8" - minimatch "^3.0.4" - multimatch "^5.0.0" - normalize-path "^3.0.0" - textextensions "^5.13.0" - -"mem-fs@^1.2.0 || ^2.0.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/mem-fs/-/mem-fs-2.2.1.tgz#c87bc8a53fb17971b129d4bcd59a9149fb78c5b1" - integrity sha512-yiAivd4xFOH/WXlUi6v/nKopBh1QLzwjFi36NK88cGt/PRXI8WeBASqY+YSjIVWvQTx3hR8zHKDBMV6hWmglNA== - dependencies: - "@types/node" "^15.6.1" - "@types/vinyl" "^2.0.4" - vinyl "^2.0.1" - vinyl-file "^3.0.0" - meow@^8.0.0: version "8.1.2" resolved "https://registry.yarnpkg.com/meow/-/meow-8.1.2.tgz#bcbe45bda0ee1729d350c03cffc8395a36c4e897" @@ -7687,16 +7965,16 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-response@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - mimic-response@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== +mimic-response@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-4.0.0.tgz#35468b19e7c75d10f5165ea25e75a5ceea7cf70f" + integrity sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg== + min-indent@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -7730,10 +8008,10 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.4: - version "9.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" - integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== +minimatch@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" @@ -7746,7 +8024,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.1.0, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: +minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== @@ -7763,7 +8041,7 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^1.3.2, minipass-fetch@^1.4.1: +minipass-fetch@^1.3.2: version "1.4.1" resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== @@ -7863,11 +8141,6 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mock-stdin@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/mock-stdin/-/mock-stdin-1.0.0.tgz#efcfaf4b18077e14541742fd758b9cae4e5365ea" - integrity sha512-tukRdb9Beu27t6dN+XztSRHq9J0B/CoAOySGzHfn8UTfmqipA5yNT/sDUEyYdAV3Hpka6Wx6kOMxuObdOex60Q== - modify-values@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" @@ -7904,6 +8177,11 @@ mute-stream@0.0.8, mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +mute-stream@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" + integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== + napi-build-utils@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" @@ -7924,11 +8202,6 @@ natural-orderby@3.0.2: resolved "https://registry.yarnpkg.com/natural-orderby/-/natural-orderby-3.0.2.tgz#1b874d685fbd68beab2c6e7d14f298e03d631ec3" integrity sha512-x7ZdOwBxZCEm9MM7+eQCjkrNLrW3rkBKNHVr78zbtqnMGVNlnDi6C/eUEYgxHNrcbu0ymvjzcwIL/6H1iHri9g== -natural-orderby@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/natural-orderby/-/natural-orderby-2.0.3.tgz#8623bc518ba162f8ff1cdb8941d74deb0fdcc016" - integrity sha512-p7KTHxU0CUrcOXe62Zfrb5Z13nLvPhSWR/so3kFulUQU0sgUll2Z0LwpsLN351eOOD+hRGu/F1g+6xDfPeD++Q== - negotiator@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -7944,19 +8217,21 @@ neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" -nock@^13.0.0: - version "13.2.1" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.1.tgz#fcf5bdb9bb9f0554a84c25d3333166c0ffd80858" - integrity sha512-CoHAabbqq/xZEknubuyQMjq6Lfi5b7RtK6SoNK6m40lebGp3yiMagWtIoYaw2s9sISD7wPuCfwFpivVHX/35RA== +nock@13.5.4: + version "13.5.4" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.5.4.tgz#8918f0addc70a63736170fef7106a9721e0dc479" + integrity sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw== dependencies: debug "^4.1.0" json-stringify-safe "^5.0.1" - lodash.set "^4.3.2" propagate "^2.0.0" node-abi@^3.3.0: @@ -8034,7 +8309,7 @@ node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== -node-gyp@8.4.1, node-gyp@8.x, node-gyp@^8.2.0: +node-gyp@8.4.1, node-gyp@8.x: version "8.4.1" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== @@ -8114,7 +8389,7 @@ normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-package-data@^3.0.0, normalize-package-data@^3.0.3: +normalize-package-data@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== @@ -8134,15 +8409,24 @@ normalize-package-data@^4.0.0: semver "^7.3.5" validate-npm-package-license "^3.0.4" +normalize-package-data@^6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz#a7bc22167fe24025412bcff0a9651eb768b03506" + integrity sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g== + dependencies: + hosted-git-info "^7.0.0" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" + normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -normalize-url@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" - integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== +normalize-url@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.0.1.tgz#9b7d96af9836577c58f5883e939365fa15623a4a" + integrity sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w== npm-bundled@^1.1.1: version "1.1.2" @@ -8158,13 +8442,6 @@ npm-bundled@^2.0.0: dependencies: npm-normalize-package-bin "^2.0.0" -npm-install-checks@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" - integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== - dependencies: - semver "^7.1.1" - npm-install-checks@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-5.0.0.tgz#5ff27d209a4e3542b8ac6b0c1db6063506248234" @@ -8191,15 +8468,6 @@ npm-package-arg@8.1.1: semver "^7.0.0" validate-npm-package-name "^3.0.0" -npm-package-arg@^8.0.1, npm-package-arg@^8.1.2, npm-package-arg@^8.1.5: - version "8.1.5" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44" - integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q== - dependencies: - hosted-git-info "^4.0.1" - semver "^7.3.4" - validate-npm-package-name "^3.0.0" - npm-package-arg@^9.0.0, npm-package-arg@^9.0.1: version "9.1.0" resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-9.1.0.tgz#a60e9f1e7c03e4e3e4e994ea87fff8b90b522987" @@ -8208,17 +8476,7 @@ npm-package-arg@^9.0.0, npm-package-arg@^9.0.1: hosted-git-info "^5.0.0" proc-log "^2.0.1" semver "^7.3.5" - validate-npm-package-name "^4.0.0" - -npm-packlist@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-3.0.0.tgz#0370df5cfc2fcc8f79b8f42b37798dd9ee32c2a9" - integrity sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ== - dependencies: - glob "^7.1.6" - ignore-walk "^4.0.1" - npm-bundled "^1.1.1" - npm-normalize-package-bin "^1.0.1" + validate-npm-package-name "^4.0.0" npm-packlist@^5.1.0, npm-packlist@^5.1.1: version "5.1.3" @@ -8230,16 +8488,6 @@ npm-packlist@^5.1.0, npm-packlist@^5.1.1: npm-bundled "^2.0.0" npm-normalize-package-bin "^2.0.0" -npm-pick-manifest@^6.0.0, npm-pick-manifest@^6.1.0, npm-pick-manifest@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148" - integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA== - dependencies: - npm-install-checks "^4.0.0" - npm-normalize-package-bin "^1.0.1" - npm-package-arg "^8.1.2" - semver "^7.3.4" - npm-pick-manifest@^7.0.0: version "7.0.2" resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-7.0.2.tgz#1d372b4e7ea7c6712316c0e99388a73ed3496e84" @@ -8250,18 +8498,6 @@ npm-pick-manifest@^7.0.0: npm-package-arg "^9.0.0" semver "^7.3.5" -npm-registry-fetch@^12.0.0, npm-registry-fetch@^12.0.1: - version "12.0.2" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz#ae583bb3c902a60dae43675b5e33b5b1f6159f1e" - integrity sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA== - dependencies: - make-fetch-happen "^10.0.1" - minipass "^3.1.6" - minipass-fetch "^1.4.1" - minipass-json-stream "^1.0.1" - minizlib "^2.1.2" - npm-package-arg "^8.1.5" - npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1, npm-registry-fetch@^13.3.0: version "13.3.1" resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz#bb078b5fa6c52774116ae501ba1af2a33166af7e" @@ -8275,7 +8511,7 @@ npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1, npm-registry-fetch@^13.3 npm-package-arg "^9.0.1" proc-log "^2.0.0" -npm-run-path@^4.0.0, npm-run-path@^4.0.1: +npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -8383,11 +8619,6 @@ object-keys@^1.0.12, object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object-treeify@^1.1.33: - version "1.1.33" - resolved "https://registry.yarnpkg.com/object-treeify/-/object-treeify-1.1.33.tgz#f06fece986830a3cba78ddd32d4c11d1f76cdf40" - integrity sha512-EFVjAYfzWqWsBMRHPMAXLCDIJnpMhdWAqR7xG6M6a2cs6PMFpl/+Z20w9zDW4vkxOFfddegBKq9Rehd0bxWE7A== - object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" @@ -8407,30 +8638,35 @@ object.values@^1.1.3: define-properties "^1.1.3" es-abstract "^1.19.1" -oclif@3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/oclif/-/oclif-3.7.2.tgz#52bcfc5fcdf996fc7f27a9d3729f63aa0c8225e3" - integrity sha512-mKzQQ4GFbsuqtzvN3nHYZu2SYLDX670Sa7UItTCOFNJY3S/e65Dcj+Nx/dBPMolESWjW6fIk8I8+kuGeiGVNXw== - dependencies: - "@oclif/core" "^2.7.1" - "@oclif/plugin-help" "^5.1.19" - "@oclif/plugin-not-found" "^2.3.7" - "@oclif/plugin-warn-if-update-available" "^2.0.14" - aws-sdk "^2.1231.0" - concurrently "^7.6.0" - debug "^4.3.3" +oclif@4.14.0: + version "4.14.0" + resolved "https://registry.yarnpkg.com/oclif/-/oclif-4.14.0.tgz#e13eb06c2eb3ab426bb2c5cf40d90e06f48d7155" + integrity sha512-k24YqM82RADFfQLEPEQqSjMweZjpdP9s3iQwSHk62AM8qdUyMd/4znbLxcscE+EczwIRmm+IcVOt9IrdIM/2zw== + dependencies: + "@aws-sdk/client-cloudfront" "^3.609.0" + "@aws-sdk/client-s3" "^3.609.0" + "@inquirer/confirm" "^3.1.11" + "@inquirer/input" "^2.1.9" + "@inquirer/select" "^2.3.10" + "@oclif/core" "^4" + "@oclif/plugin-help" "^6.2.2" + "@oclif/plugin-not-found" "^3.2.3" + "@oclif/plugin-warn-if-update-available" "^3.0.19" + async-retry "^1.3.3" + chalk "^4" + change-case "^4" + debug "^4.3.4" + ejs "^3.1.10" find-yarn-workspace-root "^2.0.0" fs-extra "^8.1" - github-slugger "^1.5.0" - got "^11" + github-slugger "^2" + got "^13" lodash "^4.17.21" - normalize-package-data "^3.0.3" - semver "^7.3.8" - shelljs "^0.8.5" - tslib "^2.3.1" - yeoman-environment "^3.11.1" - yeoman-generator "^5.6.1" - yosay "^2.0.2" + normalize-package-data "^6" + semver "^7.6.2" + sort-package-json "^2.10.0" + tiny-jsonc "^1.0.1" + validate-npm-package-name "^5.0.0" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" @@ -8487,10 +8723,10 @@ os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -p-cancelable@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" - integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== +p-cancelable@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" + integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw== p-finally@^1.0.0: version "1.0.0" @@ -8576,14 +8812,6 @@ p-timeout@^3.2.0: dependencies: p-finally "^1.0.0" -p-transform@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-transform/-/p-transform-1.3.0.tgz#2da960ba92c6a56efbe75cbd1edf3ea7b3191049" - integrity sha512-UJKdSzgd3KOnXXAtqN5+/eeHcvTn1hBkesEmElVgvO/NAYcxAvmjzIGmnNd3Tb/gRAvMBdNRFD4qAWdHxY6QXg== - dependencies: - debug "^4.3.2" - p-queue "^6.6.2" - p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -8601,31 +8829,6 @@ p-waterfall@^2.1.1: dependencies: p-reduce "^2.0.0" -pacote@^12.0.0, pacote@^12.0.2: - version "12.0.3" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-12.0.3.tgz#b6f25868deb810e7e0ddf001be88da2bcaca57c7" - integrity sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow== - dependencies: - "@npmcli/git" "^2.1.0" - "@npmcli/installed-package-contents" "^1.0.6" - "@npmcli/promise-spawn" "^1.2.0" - "@npmcli/run-script" "^2.0.0" - cacache "^15.0.5" - chownr "^2.0.0" - fs-minipass "^2.1.0" - infer-owner "^1.0.4" - minipass "^3.1.3" - mkdirp "^1.0.3" - npm-package-arg "^8.0.1" - npm-packlist "^3.0.0" - npm-pick-manifest "^6.0.0" - npm-registry-fetch "^12.0.0" - promise-retry "^2.0.1" - read-package-json-fast "^2.0.1" - rimraf "^3.0.2" - ssri "^8.0.1" - tar "^6.1.0" - pacote@^13.0.3, pacote@^13.6.1: version "13.6.2" resolved "https://registry.yarnpkg.com/pacote/-/pacote-13.6.2.tgz#0d444ba3618ab3e5cd330b451c22967bbd0ca48a" @@ -8653,10 +8856,13 @@ pacote@^13.0.3, pacote@^13.6.1: ssri "^9.0.0" tar "^6.1.11" -pad-component@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/pad-component/-/pad-component-0.0.1.tgz#ad1f22ce1bf0fdc0d6ddd908af17f351a404b8ac" - integrity sha1-rR8izhvw/cDW3dkIrxfzUaQEuKw= +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" parent-module@^1.0.0: version "1.0.1" @@ -8706,21 +8912,21 @@ parse-url@^8.1.0: dependencies: parse-path "^7.0.0" -password-prompt@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/password-prompt/-/password-prompt-1.1.2.tgz#85b2f93896c5bd9e9f2d6ff0627fa5af3dc00923" - integrity sha512-bpuBhROdrhuN3E7G/koAju0WjVw9/uQOG5Co5mokNj0MiOSBVZS1JTwM4zl55hu0WFmIEFvO9cU9sJQiBIYeIA== +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== dependencies: - ansi-escapes "^3.1.0" - cross-spawn "^6.0.5" + no-case "^3.0.4" + tslib "^2.0.3" -password-prompt@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/password-prompt/-/password-prompt-1.1.3.tgz#05e539f4e7ca4d6c865d479313f10eb9db63ee5f" - integrity sha512-HkrjG2aJlvF0t2BMH0e2LB/EHf3Lcq3fNMzy4GYHcQblAvOl+QQji1Lx7WRBMqpVK8p+KR7bCg7oqAMXtdgqyw== +path-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f" + integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg== dependencies: - ansi-escapes "^4.3.2" - cross-spawn "^7.0.3" + dot-case "^3.0.4" + tslib "^2.0.3" path-exists@^3.0.0: version "3.0.0" @@ -8737,11 +8943,6 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" @@ -8832,11 +9033,6 @@ pkg-up@^2.0.0: dependencies: find-up "^2.1.0" -possible-typed-array-names@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" - integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== - prebuild-install@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870" @@ -8874,16 +9070,6 @@ prebuild-install@^7.1.1: tar-fs "^2.0.0" tunnel-agent "^0.6.0" -preferred-pm@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/preferred-pm/-/preferred-pm-3.0.3.tgz#1b6338000371e3edbce52ef2e4f65eb2e73586d6" - integrity sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ== - dependencies: - find-up "^5.0.0" - find-yarn-workspace-root2 "1.2.16" - path-exists "^4.0.0" - which-pm "2.0.0" - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -8901,11 +9087,6 @@ prettier@2.3.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d" integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ== -pretty-bytes@^5.3.0: - version "5.6.0" - resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" - integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== - pretty-format@^29.3.1: version "29.3.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.3.1.tgz#1841cac822b02b4da8971dacb03e8a871b4722da" @@ -8924,26 +9105,16 @@ pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" -proc-log@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-1.0.0.tgz#0d927307401f69ed79341e83a0b2c9a13395eb77" - integrity sha512-aCk8AO51s+4JyuYGg3Q/a6gnrlDO09NpVWePtjp7xwphcoQ04x5WAfCyugcsbLooWcMJ87CLkD4+604IckEdhg== - proc-log@^2.0.0, proc-log@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-2.0.1.tgz#8f3f69a1f608de27878f91f5c688b225391cb685" integrity sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw== -process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: +process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== - promise-all-reject-late@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2" @@ -9027,11 +9198,6 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - punycode@^1.3.2: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -9052,11 +9218,6 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - queue-microtask@^1.2.2, queue-microtask@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -9097,7 +9258,7 @@ read-cmd-shim@^3.0.0: resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-3.0.1.tgz#868c235ec59d1de2db69e11aec885bc095aea087" integrity sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g== -read-package-json-fast@^2.0.1, read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: +read-package-json-fast@^2.0.2, read-package-json-fast@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83" integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ== @@ -9167,7 +9328,7 @@ readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.3.5, readable-stream@~2.3.6: +readable-stream@^2.0.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -9180,17 +9341,6 @@ readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.3.5, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^4.3.0: - version "4.5.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.5.2.tgz#9e7fc4c45099baeed934bff6eb97ba6cf2729e09" - integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g== - dependencies: - abort-controller "^3.0.0" - buffer "^6.0.3" - events "^3.3.0" - process "^0.11.10" - string_decoder "^1.3.0" - readdir-scoped-modules@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" @@ -9201,13 +9351,6 @@ readdir-scoped-modules@^1.1.0: graceful-fs "^4.1.2" once "^1.3.0" -rechoir@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" - integrity sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= - dependencies: - resolve "^1.1.6" - redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -9216,34 +9359,17 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -redeyed@~2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" - integrity sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs= - dependencies: - esprima "~4.0.0" - regenerator-runtime@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -replace-ext@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" - integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -resolve-alpn@^1.0.0: +resolve-alpn@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9" integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g== @@ -9270,7 +9396,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.20.0: +resolve@^1.10.0, resolve@^1.20.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -9278,12 +9404,12 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.20.0: is-core-module "^2.2.0" path-parse "^1.0.6" -responselike@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc" - integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw== +responselike@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/responselike/-/responselike-3.0.0.tgz#20decb6c298aff0dbee1c355ca95461d42823626" + integrity sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg== dependencies: - lowercase-keys "^2.0.0" + lowercase-keys "^3.0.0" restore-cursor@^3.1.0: version "3.1.0" @@ -9293,6 +9419,11 @@ restore-cursor@^3.1.0: onetime "^5.1.0" signal-exit "^3.0.2" +retry@0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -9335,7 +9466,7 @@ rollup@^4.14.1: "@rollup/rollup-win32-x64-msvc" "4.17.2" fsevents "~2.3.2" -run-async@^2.0.0, run-async@^2.4.0: +run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== @@ -9347,13 +9478,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^7.0.0, rxjs@^7.8.1: - version "7.8.1" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" - integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== - dependencies: - tslib "^2.1.0" - rxjs@^7.5.5: version "7.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" @@ -9361,6 +9485,13 @@ rxjs@^7.5.5: dependencies: tslib "^2.1.0" +rxjs@^7.8.1: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-buffer@^5.0.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -9376,22 +9507,7 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sax@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" - integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o= - -sax@>=0.6.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -scoped-regex@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-2.1.0.tgz#7b9be845d81fd9d21d1ec97c61a0b7cf86d2015f" - integrity sha512-g3WxHrqSWCZHGHlSrF51VXFdjImhwvH8ZO/pryFH56Qi0cDsZfylQa/t0jCzVQFNbNvM00HfHjkDPEuarKDSWQ== - -"semver@2 || 3 || 4 || 5", "semver@>= 5 < 6", semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", "semver@>= 5 < 6", semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -9420,18 +9536,13 @@ semver@^7.0.0, semver@^7.3.7: dependencies: lru-cache "^6.0.0" -semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== dependencies: lru-cache "^6.0.0" -semver@^7.3.8: - version "7.6.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== - semver@^7.5.3: version "7.5.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" @@ -9446,23 +9557,25 @@ semver@^7.5.4: dependencies: lru-cache "^6.0.0" +semver@^7.6.0, semver@^7.6.2: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +sentence-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f" + integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + upper-case-first "^2.0.2" + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-function-length@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" - integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - gopd "^1.0.1" - has-property-descriptors "^1.0.2" - shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -9470,13 +9583,6 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -9484,30 +9590,11 @@ shebang-command@^2.0.0: dependencies: shebang-regex "^3.0.0" -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.7.3: - version "1.8.1" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" - integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== - -shelljs@^0.8.5: - version "0.8.5" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" - integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== - dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" - shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -9532,6 +9619,11 @@ signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -9546,13 +9638,6 @@ simple-get@^4.0.0: once "^1.3.1" simple-concat "^1.0.0" -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== - dependencies: - is-arrayish "^0.3.1" - sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -9563,20 +9648,24 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@^4.0.0: +slash@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" + integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== smart-buffer@^4.1.0, smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + socks-proxy-agent@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.0.tgz#869cf2d7bd10fea96c7ad3111e81726855e285c3" @@ -9586,15 +9675,6 @@ socks-proxy-agent@^6.0.0: debug "^4.3.1" socks "^2.6.1" -socks-proxy-agent@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87" - integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew== - dependencies: - agent-base "^6.0.2" - debug "^4.3.1" - socks "^2.6.1" - socks-proxy-agent@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" @@ -9627,13 +9707,32 @@ sort-keys@^2.0.0: dependencies: is-plain-obj "^1.0.0" -sort-keys@^4.0.0, sort-keys@^4.2.0: +sort-keys@^4.0.0: version "4.2.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-4.2.0.tgz#6b7638cee42c506fff8c1cecde7376d21315be18" integrity sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg== dependencies: is-plain-obj "^2.0.0" +sort-object-keys@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45" + integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg== + +sort-package-json@^2.10.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/sort-package-json/-/sort-package-json-2.10.0.tgz#6be07424bf3b7db9fbb1bdd69e7945f301026d8a" + integrity sha512-MYecfvObMwJjjJskhxYfuOADkXp1ZMMnCFC8yhp+9HDsk7HhR336hd7eiBs96lTXfiqmUNI+WQCeCMRBhl251g== + dependencies: + detect-indent "^7.0.1" + detect-newline "^4.0.0" + get-stdin "^9.0.0" + git-hooks-list "^3.0.0" + globby "^13.1.2" + is-plain-obj "^4.1.0" + semver "^7.6.0" + sort-object-keys "^1.1.3" + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -9647,11 +9746,6 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -spawn-command@^0.0.2-1: - version "0.0.2-1" - resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" - integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A= - spdx-correct@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" @@ -9741,14 +9835,6 @@ stack-utils@^2.0.3: dependencies: escape-string-regexp "^2.0.0" -stdout-stderr@^0.1.9: - version "0.1.13" - resolved "https://registry.yarnpkg.com/stdout-stderr/-/stdout-stderr-0.1.13.tgz#54e3450f3d4c54086a49c0c7f8786a44d1844b6f" - integrity sha512-Xnt9/HHHYfjZ7NeQLvuQDyL1LnbsbddgMFKCuaQKwGCdJm8LnstZIXop+uOY36UR1UXXoHXfMbC1KlVdVd2JLA== - dependencies: - debug "^4.1.1" - strip-ansi "^6.0.0" - stream-combiner@~0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" @@ -9794,14 +9880,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -9818,7 +9896,7 @@ string.prototype.trimstart@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" -string_decoder@^1.1.1, string_decoder@^1.3.0: +string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -9839,13 +9917,6 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1: dependencies: ansi-regex "^2.0.0" -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8= - dependencies: - ansi-regex "^3.0.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -9853,28 +9924,6 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-bom-buf@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz#1cb45aaf57530f4caf86c7f75179d2c9a51dd572" - integrity sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI= - dependencies: - is-utf8 "^0.2.1" - -strip-bom-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz#f87db5ef2613f6968aa545abfe1ec728b6a829ca" - integrity sha1-+H217yYT9paKpUWr/h7HKLaoKco= - dependencies: - first-chunk-stream "^2.0.0" - strip-bom "^2.0.0" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - integrity sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= - dependencies: - is-utf8 "^0.2.0" - strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -9907,6 +9956,11 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + strong-log-transformer@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz#0f5ed78d325e0421ac6f90f7f10e691d6ae3ae10" @@ -9921,11 +9975,6 @@ stubs@^3.0.0: resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" integrity sha1-6NK6H6nJBXAwPAMLaQD31fiavls= -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -9940,14 +9989,14 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.0.0, supports-color@^8.1.0, supports-color@^8.1.1: +supports-color@^8, supports-color@^8.0.0: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" -supports-hyperlinks@2.2.0, supports-hyperlinks@^2.2.0: +supports-hyperlinks@2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== @@ -9960,14 +10009,6 @@ synchronous-promise@^2.0.13: resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.15.tgz#07ca1822b9de0001f5ff73595f3d08c4f720eb8e" integrity sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg== -taketalk@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/taketalk/-/taketalk-1.0.0.tgz#b4d4f0deed206ae7df775b129ea2ca6de52f26dd" - integrity sha1-tNTw3u0gauffd1sSnqLKbeUvJt0= - dependencies: - get-stdin "^4.0.1" - minimist "^1.1.0" - tar-fs@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" @@ -10036,11 +10077,6 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -textextensions@^5.12.0, textextensions@^5.13.0: - version "5.14.0" - resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-5.14.0.tgz#a6ff6aee5faaa751e6157d422c722a2bfd59eedf" - integrity sha512-4cAYwNFNYlIAHBUo7p6zw8POUvWbZor+/R0Tanv+rIhsauEyV9QSrEXL40pI+GfTQxKX8k6Tyw6CmdSDSmASrg== - through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -10061,6 +10097,11 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@~2.3, t resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +tiny-jsonc@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tiny-jsonc/-/tiny-jsonc-1.0.1.tgz#71de47c9d812b411e87a9f3ab4a5fe42cd8d8f9c" + integrity sha512-ik6BCxzva9DoiEfDX/li0L2cWKPPENYvixUprFdl3YPi4bZZUhDnNI9YUkacrv+uIG90dnxR5mNqaoD6UhD6Bw== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -10102,16 +10143,6 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= -tree-kill@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" - integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== - -treeverse@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-1.0.4.tgz#a6b0ebf98a1bca6846ddc7ecbc900df08cb9cd5f" - integrity sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g== - treeverse@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-2.0.0.tgz#036dcef04bc3fd79a9b79a68d4da03e882d8a9ca" @@ -10141,7 +10172,7 @@ ts-jest@29.1.1: semver "^7.5.3" yargs-parser "^21.0.1" -ts-node@10.9.1, ts-node@^10.9.1: +ts-node@10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== @@ -10195,7 +10226,12 @@ tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2, tslib@^2.1.0, tslib@^2.3.1: +tslib@^2.0.3, tslib@^2.6.2: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + +tslib@^2.1.0, tslib@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== @@ -10210,16 +10246,6 @@ tslib@^2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -tslib@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== - -tslib@^2.5.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity "sha1-JJRLotmQlA5umCxL6hR6uoAgmRM= sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==" - tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -10318,6 +10344,11 @@ unbox-primitive@^1.0.1: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + unique-filename@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" @@ -10361,11 +10392,6 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== -untildify@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" - integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== - upath@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" @@ -10379,6 +10405,20 @@ update-browserslist-db@^1.0.13: escalade "^3.1.1" picocolors "^1.0.0" +upper-case-first@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" + integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg== + dependencies: + tslib "^2.0.3" + +upper-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a" + integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg== + dependencies: + tslib "^2.0.3" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -10386,14 +10426,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" - integrity sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - urlgrey@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/urlgrey/-/urlgrey-1.0.0.tgz#72d2f904482d0b602e3c7fa599343d699bbe1017" @@ -10415,27 +10447,16 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= -util@^0.12.4: - version "0.12.5" - resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" - integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== - dependencies: - inherits "^2.0.3" - is-arguments "^1.0.4" - is-generator-function "^1.0.7" - is-typed-array "^1.1.3" - which-typed-array "^1.1.2" - -uuid@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" - integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== - uuid@8.3.2, uuid@^8.0.0, uuid@^8.3.0, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -10477,28 +10498,10 @@ validate-npm-package-name@^4.0.0: dependencies: builtins "^5.0.0" -vinyl-file@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/vinyl-file/-/vinyl-file-3.0.0.tgz#b104d9e4409ffa325faadd520642d0a3b488b365" - integrity sha1-sQTZ5ECf+jJfqt1SBkLQo7SIs2U= - dependencies: - graceful-fs "^4.1.2" - pify "^2.3.0" - strip-bom-buf "^1.0.0" - strip-bom-stream "^2.0.0" - vinyl "^2.0.1" - -vinyl@^2.0.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974" - integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw== - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" +validate-npm-package-name@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" + integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== walk-up-path@^1.0.0: version "1.0.0" @@ -10543,32 +10546,6 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-pm@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-pm/-/which-pm-2.0.0.tgz#8245609ecfe64bf751d0eef2f376d83bf1ddb7ae" - integrity sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w== - dependencies: - load-yaml-file "^0.2.0" - path-exists "^4.0.0" - -which-typed-array@^1.1.14, which-typed-array@^1.1.2: - version "1.1.15" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" - integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== - dependencies: - available-typed-arrays "^1.0.7" - call-bind "^1.0.7" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.2" - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -10595,13 +10572,14 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" wrap-ansi@^7.0.0: version "7.0.0" @@ -10690,19 +10668,6 @@ ws@8.12.1: resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.1.tgz#c51e583d79140b5e42e39be48c934131942d4a8f" integrity sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew== -xml2js@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" - integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== - xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -10774,68 +10739,6 @@ yarn@^1.22.10: resolved "https://registry.yarnpkg.com/yarn/-/yarn-1.22.17.tgz#bf910747d22497b573131f7341c0e1d15c74036c" integrity sha512-H0p241BXaH0UN9IeH//RT82tl5PfNraVpSpEoW+ET7lmopNC61eZ+A+IDvU8FM6Go5vx162SncDL8J1ZjRBriQ== -yeoman-environment@^3.11.1: - version "3.19.3" - resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-3.19.3.tgz#49c2339805fdf695fac42c88334a1daa94ee8b6c" - integrity sha512-/+ODrTUHtlDPRH9qIC0JREH8+7nsRcjDl3Bxn2Xo/rvAaVvixH5275jHwg0C85g4QsF4P6M2ojfScPPAl+pLAg== - dependencies: - "@npmcli/arborist" "^4.0.4" - are-we-there-yet "^2.0.0" - arrify "^2.0.1" - binaryextensions "^4.15.0" - chalk "^4.1.0" - cli-table "^0.3.1" - commander "7.1.0" - dateformat "^4.5.0" - debug "^4.1.1" - diff "^5.0.0" - error "^10.4.0" - escape-string-regexp "^4.0.0" - execa "^5.0.0" - find-up "^5.0.0" - globby "^11.0.1" - grouped-queue "^2.0.0" - inquirer "^8.0.0" - is-scoped "^2.1.0" - isbinaryfile "^4.0.10" - lodash "^4.17.10" - log-symbols "^4.0.0" - mem-fs "^1.2.0 || ^2.0.0" - mem-fs-editor "^8.1.2 || ^9.0.0" - minimatch "^3.0.4" - npmlog "^5.0.1" - p-queue "^6.6.2" - p-transform "^1.3.0" - pacote "^12.0.2" - preferred-pm "^3.0.3" - pretty-bytes "^5.3.0" - readable-stream "^4.3.0" - semver "^7.1.3" - slash "^3.0.0" - strip-ansi "^6.0.0" - text-table "^0.2.0" - textextensions "^5.12.0" - untildify "^4.0.0" - -yeoman-generator@^5.6.1: - version "5.6.1" - resolved "https://registry.yarnpkg.com/yeoman-generator/-/yeoman-generator-5.6.1.tgz#850fd266a5ab26d9d1cb9c46ad646f06eade4a1d" - integrity sha512-XllgFvmDEwoPMq2rKtL4/N52WlINJW6a3I3XtlCrMb3/dqO5dW0nPNgR0L3IIUIdf9y1EHb1ZFMs2Qp3ZEEFxg== - dependencies: - chalk "^4.1.0" - dargs "^7.0.0" - debug "^4.1.1" - execa "^4.1.0" - github-username "^6.0.0" - lodash "^4.17.11" - minimist "^1.2.5" - read-pkg-up "^7.0.1" - run-async "^2.0.0" - semver "^7.2.1" - shelljs "^0.8.5" - sort-keys "^4.2.0" - text-table "^0.2.0" - yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" @@ -10846,20 +10749,10 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -yosay@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/yosay/-/yosay-2.0.2.tgz#a7017e764cd88d64a1ae64812201de5b157adf6d" - integrity sha512-avX6nz2esp7IMXGag4gu6OyQBsMh/SEn+ZybGu3yKPlOTE6z9qJrzG/0X5vCq/e0rPFy0CUYCze0G5hL310ibA== - dependencies: - ansi-regex "^2.0.0" - ansi-styles "^3.0.0" - chalk "^1.0.0" - cli-boxes "^1.0.0" - pad-component "0.0.1" - string-width "^2.0.0" - strip-ansi "^3.0.0" - taketalk "^1.0.0" - wrap-ansi "^2.0.0" +yoctocolors-cjs@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz#f4b905a840a37506813a7acaa28febe97767a242" + integrity sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA== yup@0.29.3: version "0.29.3" From 9b5d6160d5abf386a7a007dfaee1ab559417899a Mon Sep 17 00:00:00 2001 From: andrea Date: Tue, 9 Jul 2024 18:46:21 -0700 Subject: [PATCH 44/81] Worker pool: use the transfer feature and shared buffers for message posting This changes the way the worker pool and the workers communicate in the following way: - Buffers sent to/from workers are now "transferred". This is a feature of `MessagePort.postMessage()` that allows to transfer ownership of certain objects to the receiving side, avoiding cloning, thus improving the performance of every worker. - It's now possible to send to the workers an optional `SharedArrayBuffer` as an input (in addition to the `Buffer` that is already supported). The `BackgroundNoteDecryptor` used by the wallet scanner has been updated to make use of `SharedArrayBuffer` to store the account keys. --- ironfish-rust-nodejs/index.d.ts | 6 +- .../src/structs/note_encrypted.rs | 17 +- ironfish-rust-nodejs/tests/demo.test.slow.ts | 6 +- ironfish-rust/src/keys/view_keys.rs | 9 +- ironfish/src/primitives/noteEncrypted.ts | 26 ++- ironfish/src/rpc/routes/wallet/utils.ts | 6 +- ironfish/src/wallet/scanner/noteDecryptor.ts | 53 +++-- ironfish/src/wallet/wallet.ts | 6 +- .../src/workerPool/tasks/decryptNotes.test.ts | 81 ++++++-- ironfish/src/workerPool/tasks/decryptNotes.ts | 188 ++++++++++++++---- .../src/workerPool/tasks/workerMessage.ts | 25 +++ .../tasks/workerMessages.test.perf.ts | 6 +- ironfish/src/workerPool/worker.ts | 25 ++- 13 files changed, 344 insertions(+), 110 deletions(-) diff --git a/ironfish-rust-nodejs/index.d.ts b/ironfish-rust-nodejs/index.d.ts index dd8e93485e..cb4be65e4f 100644 --- a/ironfish-rust-nodejs/index.d.ts +++ b/ironfish-rust-nodejs/index.d.ts @@ -132,10 +132,10 @@ export class NoteEncrypted { */ static combineHash(depth: number, jsLeft: Buffer, jsRight: Buffer): Buffer /** Returns undefined if the note was unable to be decrypted with the given key. */ - decryptNoteForOwner(incomingHexKey: string): Buffer | null - decryptNoteForOwners(incomingHexKeys: Array): Array + decryptNoteForOwner(incomingViewKey: Buffer): Buffer | null + decryptNoteForOwners(incomingViewKeys: Array): Array /** Returns undefined if the note was unable to be decrypted with the given key. */ - decryptNoteForSpender(outgoingHexKey: string): Buffer | null + decryptNoteForSpender(outgoingViewKey: Buffer): Buffer | null } export type NativeNote = Note export class Note { diff --git a/ironfish-rust-nodejs/src/structs/note_encrypted.rs b/ironfish-rust-nodejs/src/structs/note_encrypted.rs index 07a8d9363e..cd0597bf3e 100644 --- a/ironfish-rust-nodejs/src/structs/note_encrypted.rs +++ b/ironfish-rust-nodejs/src/structs/note_encrypted.rs @@ -134,9 +134,9 @@ impl NativeNoteEncrypted { /// Returns undefined if the note was unable to be decrypted with the given key. #[napi] - pub fn decrypt_note_for_owner(&self, incoming_hex_key: String) -> Result> { + pub fn decrypt_note_for_owner(&self, incoming_view_key: JsBuffer) -> Result> { let incoming_view_key = - IncomingViewKey::from_hex(&incoming_hex_key).map_err(to_napi_err)?; + IncomingViewKey::read(&*incoming_view_key.into_value()?).map_err(to_napi_err)?; let decrypted_note = self.note.decrypt_note_for_owner(&incoming_view_key); decrypted_note_to_buffer(decrypted_note).map_err(to_napi_err) } @@ -144,21 +144,20 @@ impl NativeNoteEncrypted { #[napi] pub fn decrypt_note_for_owners( &self, - incoming_hex_keys: Vec, + incoming_view_keys: Vec, ) -> Result>> { - let incoming_view_keys = try_map(&incoming_hex_keys[..], |hex_key| { - IncomingViewKey::from_hex(hex_key) - }) - .map_err(to_napi_err)?; + let incoming_view_keys = try_map(incoming_view_keys, |incoming_view_key| { + IncomingViewKey::read(&*incoming_view_key.into_value()?).map_err(to_napi_err) + })?; let decrypted_notes = self.note.decrypt_note_for_owners(&incoming_view_keys); try_map(decrypted_notes, decrypted_note_to_buffer).map_err(to_napi_err) } /// Returns undefined if the note was unable to be decrypted with the given key. #[napi] - pub fn decrypt_note_for_spender(&self, outgoing_hex_key: String) -> Result> { + pub fn decrypt_note_for_spender(&self, outgoing_view_key: JsBuffer) -> Result> { let outgoing_view_key = - OutgoingViewKey::from_hex(&outgoing_hex_key).map_err(to_napi_err)?; + OutgoingViewKey::read(&*outgoing_view_key.into_value()?).map_err(to_napi_err)?; let decrypted_note = self.note.decrypt_note_for_spender(&outgoing_view_key); decrypted_note_to_buffer(decrypted_note).map_err(to_napi_err) } diff --git a/ironfish-rust-nodejs/tests/demo.test.slow.ts b/ironfish-rust-nodejs/tests/demo.test.slow.ts index 3115b6b1ee..3be016edf7 100644 --- a/ironfish-rust-nodejs/tests/demo.test.slow.ts +++ b/ironfish-rust-nodejs/tests/demo.test.slow.ts @@ -76,11 +76,11 @@ describe('Demonstrate the Sapling API', () => { expect(encryptedNote.hash().byteLength).toBe(32) expect(encryptedNote.equals(encryptedNote)).toBe(true) - const decryptedNoteBuffer = encryptedNote.decryptNoteForOwner(key.incomingViewKey) + const decryptedNoteBuffer = encryptedNote.decryptNoteForOwner(Buffer.from(key.incomingViewKey, 'hex')) expect(decryptedNoteBuffer).toBeInstanceOf(Buffer) expect(decryptedNoteBuffer!.byteLength).toBe(DECRYPTED_NOTE_LENGTH) - const decryptedSpenderNote = encryptedNote.decryptNoteForSpender(key.outgoingViewKey) + const decryptedSpenderNote = encryptedNote.decryptNoteForSpender(Buffer.from(key.outgoingViewKey, 'hex')) expect(decryptedSpenderNote).toBe(null) const decryptedNote = Note.deserialize(decryptedNoteBuffer!) @@ -104,7 +104,7 @@ describe('Demonstrate the Sapling API', () => { const transaction = new Transaction(LATEST_TRANSACTION_VERSION) transaction.setExpiration(10) const encryptedNote = new NoteEncrypted(postedMinersFeeTransaction.getNote(0)) - const decryptedNote = Note.deserialize(encryptedNote.decryptNoteForOwner(key.incomingViewKey)!) + const decryptedNote = Note.deserialize(encryptedNote.decryptNoteForOwner(Buffer.from(key.incomingViewKey, 'hex'))!) const newNote = new Note(recipientKey.publicAddress, 15n, Buffer.from('receive'), Asset.nativeId(), minersFeeNote.owner()) let currentHash = encryptedNote.hash() diff --git a/ironfish-rust/src/keys/view_keys.rs b/ironfish-rust/src/keys/view_keys.rs index 0c32d90299..68b9f26e3e 100644 --- a/ironfish-rust/src/keys/view_keys.rs +++ b/ironfish-rust/src/keys/view_keys.rs @@ -37,7 +37,7 @@ pub struct IncomingViewKey { impl IncomingViewKey { /// load view key from a Read implementation - pub fn read(reader: &mut R) -> Result { + pub fn read(reader: R) -> Result { let view_key = read_scalar(reader)?; Ok(IncomingViewKey { view_key }) } @@ -174,6 +174,13 @@ pub struct OutgoingViewKey { } impl OutgoingViewKey { + /// load view key from a Read implementation + pub fn read(mut reader: R) -> Result { + let mut view_key = [0u8; 32]; + reader.read_exact(&mut view_key)?; + Ok(OutgoingViewKey { view_key }) + } + /// Load a key from a string of hexadecimal digits pub fn from_hex(value: &str) -> Result { match hex_to_bytes(value) { diff --git a/ironfish/src/primitives/noteEncrypted.ts b/ironfish/src/primitives/noteEncrypted.ts index dd4d39921f..9dcd3744db 100644 --- a/ironfish/src/primitives/noteEncrypted.ts +++ b/ironfish/src/primitives/noteEncrypted.ts @@ -15,6 +15,14 @@ export type NoteEncryptedHash = Buffer export type SerializedNoteEncryptedHash = Buffer export type SerializedNoteEncrypted = Buffer +const ensureBuffer = (value: Buffer | string): Buffer => { + if (typeof value === 'string') { + return Buffer.from(value, 'hex') + } else { + return value + } +} + export class NoteEncrypted { private readonly noteEncryptedSerialized: Buffer @@ -81,28 +89,28 @@ export class NoteEncrypted { } } - decryptNoteForOwner(ownerHexKey: string): Note | undefined { - const note = this.takeReference().decryptNoteForOwner(ownerHexKey) + decryptNoteForOwner(incomingViewKey: Buffer | string): Note | undefined { + const note = this.takeReference().decryptNoteForOwner(ensureBuffer(incomingViewKey)) this.returnReference() if (note) { return new Note(note) } } - decryptNoteForOwners(ownerHexKeys: Array): Array { - if (ownerHexKeys.length === 0) { + decryptNoteForOwners(incomingViewKeys: Array): Array { + if (incomingViewKeys.length === 0) { return [] - } else if (ownerHexKeys.length === 1) { - return [this.decryptNoteForOwner(ownerHexKeys[0])] + } else if (incomingViewKeys.length === 1) { + return [this.decryptNoteForOwner(incomingViewKeys[0])] } - const notes = this.takeReference().decryptNoteForOwners(ownerHexKeys) + const notes = this.takeReference().decryptNoteForOwners(incomingViewKeys) this.returnReference() return notes.map((note) => (note ? new Note(note) : undefined)) } - decryptNoteForSpender(spenderHexKey: string): Note | undefined { - const note = this.takeReference().decryptNoteForSpender(spenderHexKey) + decryptNoteForSpender(outgoingViewKey: Buffer | string): Note | undefined { + const note = this.takeReference().decryptNoteForSpender(ensureBuffer(outgoingViewKey)) this.returnReference() if (note) { return new Note(note) diff --git a/ironfish/src/rpc/routes/wallet/utils.ts b/ironfish/src/rpc/routes/wallet/utils.ts index 55b76fdf9a..a236bac21c 100644 --- a/ironfish/src/rpc/routes/wallet/utils.ts +++ b/ironfish/src/rpc/routes/wallet/utils.ts @@ -41,9 +41,9 @@ export async function getTransactionNotes( const accountKeys = [ { accountId: account.id, - incomingViewKey: account.incomingViewKey, - outgoingViewKey: account.outgoingViewKey, - viewKey: account.viewKey, + incomingViewKey: Buffer.from(account.incomingViewKey, 'hex'), + outgoingViewKey: Buffer.from(account.outgoingViewKey, 'hex'), + viewKey: Buffer.from(account.viewKey, 'hex'), }, ] diff --git a/ironfish/src/wallet/scanner/noteDecryptor.ts b/ironfish/src/wallet/scanner/noteDecryptor.ts index d74afc3de8..bc409c48b2 100644 --- a/ironfish/src/wallet/scanner/noteDecryptor.ts +++ b/ironfish/src/wallet/scanner/noteDecryptor.ts @@ -14,6 +14,7 @@ import { DecryptNotesOptions, DecryptNotesRequest, DecryptNotesResponse, + DecryptNotesSharedAccountKeys, } from '../../workerPool/tasks/decryptNotes' import { JobAbortedError } from '../../workerPool/tasks/jobAbort' import { Account } from '../account/account' @@ -24,11 +25,11 @@ export type DecryptNotesFromTransactionsCallback = ( transactions: Array<{ transaction: Transaction; decryptedNotes: Array }>, ) => Promise -type DecryptQueueValue = { +type DecryptQueueItem = { job: Job - accounts: ReadonlyArray blockHeader: BlockHeader transactions: ReadonlyArray + accounts: ReadonlyArray callback: DecryptNotesFromTransactionsCallback } @@ -43,7 +44,10 @@ export class BackgroundNoteDecryptor { private readonly workerPool: WorkerPool private readonly options: DecryptNotesOptions - private readonly decryptQueue: AsyncQueue + private readonly decryptQueue: AsyncQueue + + private accounts: ReadonlyArray + private sharedAccountKeys: DecryptNotesSharedAccountKeys constructor(workerPool: WorkerPool, config: Config, options: DecryptNotesOptions) { this.workerPool = workerPool @@ -56,6 +60,9 @@ export class BackgroundNoteDecryptor { } queueSize = Math.max(queueSize, 1) this.decryptQueue = new AsyncQueue(queueSize) + + this.accounts = [] + this.sharedAccountKeys = new DecryptNotesSharedAccountKeys([]) } start(abort?: AbortController) { @@ -84,7 +91,7 @@ export class BackgroundNoteDecryptor { } private async decryptLoop(): Promise { - let resolve: (value: DecryptQueueValue | void) => unknown + let resolve: (value: DecryptQueueItem | void) => unknown let reject: (reason?: unknown) => void this.onStopped.then( @@ -98,7 +105,7 @@ export class BackgroundNoteDecryptor { this.triggerFlushed = null } - const [promise, resolveNew, rejectNew] = PromiseUtils.split() + const [promise, resolveNew, rejectNew] = PromiseUtils.split() resolve = resolveNew reject = rejectNew @@ -112,7 +119,7 @@ export class BackgroundNoteDecryptor { break } - const { job, accounts, blockHeader, transactions, callback } = item + const { job, blockHeader, transactions, accounts, callback } = item let decryptNotesResponse try { @@ -166,15 +173,12 @@ export class BackgroundNoteDecryptor { throw new Error('decryptor was not started') } + this.updateAccounts(accounts) + if (!this.triggerFlushed) { this.onFlushed = new Promise((resolve) => (this.triggerFlushed = resolve)) } - const accountKeys = accounts.map((account) => ({ - incomingViewKey: account.incomingViewKey, - outgoingViewKey: account.outgoingViewKey, - viewKey: account.viewKey, - })) Assert.isNotNull(blockHeader.noteSize) const encryptedNotes = [] @@ -190,7 +194,7 @@ export class BackgroundNoteDecryptor { } const decryptNotesRequest = new DecryptNotesRequest( - accountKeys, + this.sharedAccountKeys, encryptedNotes, this.options, ) @@ -198,12 +202,35 @@ export class BackgroundNoteDecryptor { return this.decryptQueue.push({ job, - accounts, blockHeader, transactions, + accounts: this.accounts, callback, }) } + + private updateAccounts(newAccounts: ReadonlyArray) { + if ( + newAccounts.length === this.accounts.length && + newAccounts.every((account, index) => account === this.accounts[index]) + ) { + // No change + return + } + + // Because `decryptLoop` does not use `this.accounts` or + // `this.sharedAccountKeys` directly, we can swap their value without the + // need to flush the queue. This is safe as long as the value is not + // mutated. + this.accounts = newAccounts + this.sharedAccountKeys = new DecryptNotesSharedAccountKeys( + newAccounts.map((account) => ({ + incomingViewKey: Buffer.from(account.incomingViewKey, 'hex'), + outgoingViewKey: Buffer.from(account.outgoingViewKey, 'hex'), + viewKey: Buffer.from(account.viewKey, 'hex'), + })), + ) + } } /** diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index b1e18f7cb4..ecf96dc84e 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.ts @@ -401,9 +401,9 @@ export class Wallet { ): Promise>> { const accountKeys = accounts.map((account) => ({ accountId: account.id, - incomingViewKey: account.incomingViewKey, - outgoingViewKey: account.outgoingViewKey, - viewKey: account.viewKey, + incomingViewKey: Buffer.from(account.incomingViewKey, 'hex'), + outgoingViewKey: Buffer.from(account.outgoingViewKey, 'hex'), + viewKey: Buffer.from(account.viewKey, 'hex'), })) return this.workerPool.decryptNotes(accountKeys, encryptedNotes, { diff --git a/ironfish/src/workerPool/tasks/decryptNotes.test.ts b/ironfish/src/workerPool/tasks/decryptNotes.test.ts index fd8d9ee536..96a9e47cea 100644 --- a/ironfish/src/workerPool/tasks/decryptNotes.test.ts +++ b/ironfish/src/workerPool/tasks/decryptNotes.test.ts @@ -12,16 +12,21 @@ import { } from '../../testUtilities' import { ACCOUNT_KEY_LENGTH } from '../../wallet' import { VIEW_KEY_LENGTH } from '../../wallet/walletdb/accountValue' -import { DecryptNotesRequest, DecryptNotesResponse, DecryptNotesTask } from './decryptNotes' +import { + DecryptNotesRequest, + DecryptNotesResponse, + DecryptNotesSharedAccountKeys, + DecryptNotesTask, +} from './decryptNotes' describe('DecryptNotesRequest', () => { it('serializes the object to a buffer and deserializes to the original object', () => { const request = new DecryptNotesRequest( [ { - incomingViewKey: Buffer.alloc(ACCOUNT_KEY_LENGTH, 1).toString('hex'), - outgoingViewKey: Buffer.alloc(ACCOUNT_KEY_LENGTH, 1).toString('hex'), - viewKey: Buffer.alloc(VIEW_KEY_LENGTH, 1).toString('hex'), + incomingViewKey: Buffer.alloc(ACCOUNT_KEY_LENGTH, 1), + outgoingViewKey: Buffer.alloc(ACCOUNT_KEY_LENGTH, 1), + viewKey: Buffer.alloc(VIEW_KEY_LENGTH, 1), }, ], [ @@ -37,7 +42,43 @@ describe('DecryptNotesRequest', () => { 0, ) const buffer = serializePayloadToBuffer(request) - const deserializedRequest = DecryptNotesRequest.deserializePayload(request.jobId, buffer) + const deserializedRequest = DecryptNotesRequest.deserializePayload( + request.jobId, + buffer, + null, + ) + expect(deserializedRequest).toEqual(request) + }) + + it('serializes the object to a buffer and deserializes to the original object with shared memory keys', () => { + const sharedKeys = new DecryptNotesSharedAccountKeys([ + { + incomingViewKey: Buffer.alloc(ACCOUNT_KEY_LENGTH, 1), + outgoingViewKey: Buffer.alloc(ACCOUNT_KEY_LENGTH, 1), + viewKey: Buffer.alloc(VIEW_KEY_LENGTH, 1), + }, + ]) + const request = new DecryptNotesRequest( + sharedKeys, + [ + { + serializedNote: Buffer.alloc(ENCRYPTED_NOTE_LENGTH, 1), + currentNoteIndex: 2, + }, + ], + { + decryptForSpender: true, + skipNoteValidation: false, + }, + 0, + ) + const buffer = serializePayloadToBuffer(request) + const sharedMemory = request.getSharedMemoryPayload() + const deserializedRequest = DecryptNotesRequest.deserializePayload( + request.jobId, + buffer, + sharedMemory, + ) expect(deserializedRequest).toEqual(request) }) @@ -47,9 +88,9 @@ describe('DecryptNotesRequest', () => { const request = new DecryptNotesRequest( Array.from({ length: numAccounts }, () => ({ - incomingViewKey: Buffer.alloc(ACCOUNT_KEY_LENGTH, 1).toString('hex'), - outgoingViewKey: Buffer.alloc(ACCOUNT_KEY_LENGTH, 1).toString('hex'), - viewKey: Buffer.alloc(VIEW_KEY_LENGTH, 1).toString('hex'), + incomingViewKey: Buffer.alloc(ACCOUNT_KEY_LENGTH, 1), + outgoingViewKey: Buffer.alloc(ACCOUNT_KEY_LENGTH, 1), + viewKey: Buffer.alloc(VIEW_KEY_LENGTH, 1), })), Array.from({ length: numNotes }, () => ({ serializedNote: Buffer.alloc(ENCRYPTED_NOTE_LENGTH, 1), @@ -59,7 +100,11 @@ describe('DecryptNotesRequest', () => { 0, ) const buffer = serializePayloadToBuffer(request) - const deserializedRequest = DecryptNotesRequest.deserializePayload(request.jobId, buffer) + const deserializedRequest = DecryptNotesRequest.deserializePayload( + request.jobId, + buffer, + null, + ) expect(deserializedRequest.encryptedNotes).toHaveLength(numNotes) expect(deserializedRequest.accountKeys).toHaveLength(numAccounts) }) @@ -150,9 +195,9 @@ describe('DecryptNotesTask', () => { const request = new DecryptNotesRequest( [ { - incomingViewKey: account.incomingViewKey, - outgoingViewKey: account.outgoingViewKey, - viewKey: account.viewKey, + incomingViewKey: Buffer.from(account.incomingViewKey, 'hex'), + outgoingViewKey: Buffer.from(account.outgoingViewKey, 'hex'), + viewKey: Buffer.from(account.viewKey, 'hex'), }, ], [ @@ -193,9 +238,9 @@ describe('DecryptNotesTask', () => { const requestSpender = new DecryptNotesRequest( [ { - incomingViewKey: accountA.incomingViewKey, - outgoingViewKey: accountA.outgoingViewKey, - viewKey: accountA.viewKey, + incomingViewKey: Buffer.from(accountA.incomingViewKey, 'hex'), + outgoingViewKey: Buffer.from(accountA.outgoingViewKey, 'hex'), + viewKey: Buffer.from(accountA.viewKey, 'hex'), }, ], [ @@ -223,9 +268,9 @@ describe('DecryptNotesTask', () => { const requestNoSpender = new DecryptNotesRequest( [ { - incomingViewKey: accountA.incomingViewKey, - outgoingViewKey: accountA.outgoingViewKey, - viewKey: accountA.viewKey, + incomingViewKey: Buffer.from(accountA.incomingViewKey, 'hex'), + outgoingViewKey: Buffer.from(accountA.outgoingViewKey, 'hex'), + viewKey: Buffer.from(accountA.viewKey, 'hex'), }, ], [ diff --git a/ironfish/src/workerPool/tasks/decryptNotes.ts b/ironfish/src/workerPool/tasks/decryptNotes.ts index 615474e3a8..d6277c07de 100644 --- a/ironfish/src/workerPool/tasks/decryptNotes.ts +++ b/ironfish/src/workerPool/tasks/decryptNotes.ts @@ -10,15 +10,19 @@ import { VIEW_KEY_LENGTH } from '../../wallet/walletdb/accountValue' import { WorkerMessage, WorkerMessageType } from './workerMessage' import { WorkerTask } from './workerTask' +const NO_NOTE_INDEX: number = (1 << 32) - 1 + +const ACCOUNT_KEY_SIZE: number = ACCOUNT_KEY_LENGTH + ACCOUNT_KEY_LENGTH + VIEW_KEY_LENGTH + export interface DecryptNotesOptions { decryptForSpender: boolean skipNoteValidation?: boolean } export interface DecryptNotesAccountKey { - incomingViewKey: string - outgoingViewKey: string - viewKey: string + incomingViewKey: Buffer + outgoingViewKey: Buffer + viewKey: Buffer } export interface DecryptNotesItem { @@ -34,15 +38,91 @@ export interface DecryptedNote { serializedNote: Buffer } -const NO_NOTE_INDEX: number = (1 << 32) - 1 +export type DecryptNotesAccountKeys = + | DecryptNotesInlineAccountKeys + | DecryptNotesSharedAccountKeys +export type ReadonlyDecryptNotesAccountKeys = + | ReadonlyDecryptNotesInlineAccountKeys + | DecryptNotesSharedAccountKeys + +export type DecryptNotesInlineAccountKeys = Array +export type ReadonlyDecryptNotesInlineAccountKeys = ReadonlyArray + +export class DecryptNotesSharedAccountKeys { + readonly length: number + readonly sharedBuffer: SharedArrayBuffer + + constructor(accountKeys: ReadonlyArray | SharedArrayBuffer) { + if (accountKeys instanceof SharedArrayBuffer) { + this.length = Math.trunc(accountKeys.byteLength / ACCOUNT_KEY_SIZE) + this.sharedBuffer = accountKeys + } else { + this.length = accountKeys.length + this.sharedBuffer = DecryptNotesSharedAccountKeys.serialize(accountKeys) + } + } + + at(index: number): DecryptNotesAccountKey | undefined { + if (index >= this.length) { + return undefined + } + + const incomingViewKeyStart = index * ACCOUNT_KEY_LENGTH + const outgoingViewKeyStart = this.length * ACCOUNT_KEY_LENGTH + index * ACCOUNT_KEY_LENGTH + const viewKeyStart = 2 * this.length * ACCOUNT_KEY_LENGTH + index * VIEW_KEY_LENGTH + + return { + incomingViewKey: Buffer.from(this.sharedBuffer, incomingViewKeyStart, ACCOUNT_KEY_LENGTH), + outgoingViewKey: Buffer.from(this.sharedBuffer, outgoingViewKeyStart, ACCOUNT_KEY_LENGTH), + viewKey: Buffer.from(this.sharedBuffer, viewKeyStart, VIEW_KEY_LENGTH), + } + } + + map(fn: (key: DecryptNotesAccountKey) => T): Array { + const result = new Array() + for (let index = 0; index < this.length; index++) { + const key = this.at(index) + Assert.isNotUndefined(key) + result.push(fn(key)) + } + return result + } + + private static serialize( + accountKeys: ReadonlyArray, + ): SharedArrayBuffer { + const size = ACCOUNT_KEY_SIZE * accountKeys.length + + const buffer = new SharedArrayBuffer(size) + const array = new Uint8Array(buffer) + let offset = 0 + + const write = (bytes: Buffer) => { + array.set(bytes, offset) + offset += bytes.length + } + + for (const key of accountKeys) { + write(key.incomingViewKey) + } + for (const key of accountKeys) { + write(key.outgoingViewKey) + } + for (const key of accountKeys) { + write(key.viewKey) + } + + return buffer + } +} export class DecryptNotesRequest extends WorkerMessage { - readonly accountKeys: ReadonlyArray + readonly accountKeys: ReadonlyDecryptNotesAccountKeys readonly encryptedNotes: ReadonlyArray readonly options: DecryptNotesOptions constructor( - accountKeys: ReadonlyArray, + accountKeys: ReadonlyDecryptNotesAccountKeys, encryptedNotes: ReadonlyArray, options: DecryptNotesOptions, jobId?: number, @@ -56,14 +136,17 @@ export class DecryptNotesRequest extends WorkerMessage { serializePayload(bw: bufio.StaticWriter | bufio.BufferWriter): void { const flags = (Number(this.options.decryptForSpender) << 0) | - (Number(this.options.skipNoteValidation ?? false) << 1) + (Number(this.options.skipNoteValidation ?? false) << 1) | + (Number(this.accountKeys instanceof DecryptNotesSharedAccountKeys) << 2) bw.writeU8(flags) - bw.writeU32(this.accountKeys.length) - for (const key of this.accountKeys) { - bw.writeBytes(Buffer.from(key.incomingViewKey, 'hex')) - bw.writeBytes(Buffer.from(key.outgoingViewKey, 'hex')) - bw.writeBytes(Buffer.from(key.viewKey, 'hex')) + if (!(this.accountKeys instanceof DecryptNotesSharedAccountKeys)) { + bw.writeU32(this.accountKeys.length) + for (const key of this.accountKeys) { + bw.writeBytes(key.incomingViewKey) + bw.writeBytes(key.outgoingViewKey) + bw.writeBytes(key.viewKey) + } } for (const note of this.encryptedNotes) { @@ -72,7 +155,19 @@ export class DecryptNotesRequest extends WorkerMessage { } } - static deserializePayload(jobId: number, buffer: Buffer): DecryptNotesRequest { + getSharedMemoryPayload(): SharedArrayBuffer | null { + if (this.accountKeys instanceof DecryptNotesSharedAccountKeys) { + return this.accountKeys.sharedBuffer + } else { + return null + } + } + + static deserializePayload( + jobId: number, + buffer: Buffer, + sharedAccountKeys: SharedArrayBuffer | null, + ): DecryptNotesRequest { const reader = bufio.read(buffer, true) const flags = reader.readU8() @@ -80,18 +175,31 @@ export class DecryptNotesRequest extends WorkerMessage { decryptForSpender: !!(flags & (1 << 0)), skipNoteValidation: !!(flags & (1 << 1)), } + const hasSharedAccountKeys = flags & (1 << 2) - const accountKeys = [] - const encryptedNotes = [] - - const keysLength = reader.readU32() - for (let i = 0; i < keysLength; i++) { - const incomingViewKey = reader.readBytes(ACCOUNT_KEY_LENGTH).toString('hex') - const outgoingViewKey = reader.readBytes(ACCOUNT_KEY_LENGTH).toString('hex') - const viewKey = reader.readBytes(VIEW_KEY_LENGTH).toString('hex') - accountKeys.push({ incomingViewKey, outgoingViewKey, viewKey }) + let accountKeys: DecryptNotesAccountKeys + if (hasSharedAccountKeys) { + Assert.isNotNull( + sharedAccountKeys, + 'expected account keys to be provided as a SharedArrayBuffer', + ) + accountKeys = new DecryptNotesSharedAccountKeys(sharedAccountKeys) + } else { + Assert.isNull( + sharedAccountKeys, + 'account keys are already inline in the message, they should not be provided as a SharedArrayBuffer', + ) + const keysLength = reader.readU32() + accountKeys = new Array() + for (let i = 0; i < keysLength; i++) { + const incomingViewKey = reader.readBytes(ACCOUNT_KEY_LENGTH) + const outgoingViewKey = reader.readBytes(ACCOUNT_KEY_LENGTH) + const viewKey = reader.readBytes(VIEW_KEY_LENGTH) + accountKeys.push({ incomingViewKey, outgoingViewKey, viewKey }) + } } + const encryptedNotes = [] while (reader.left() > 0) { const serializedNote = reader.readBytes(ENCRYPTED_NOTE_LENGTH) let currentNoteIndex: number | null = reader.readU32() @@ -109,15 +217,14 @@ export class DecryptNotesRequest extends WorkerMessage { getSize(): number { const optionsSize = 1 - const keySize = ACCOUNT_KEY_LENGTH + ACCOUNT_KEY_LENGTH + VIEW_KEY_LENGTH const noteSize = ENCRYPTED_NOTE_LENGTH + 4 + let accountKeysSize = 0 - return ( - optionsSize + - 4 + - keySize * this.accountKeys.length + - noteSize * this.encryptedNotes.length - ) + if (!(this.accountKeys instanceof DecryptNotesSharedAccountKeys)) { + accountKeysSize += 4 + ACCOUNT_KEY_SIZE * this.accountKeys.length + } + + return optionsSize + accountKeysSize + noteSize * this.encryptedNotes.length } } @@ -279,27 +386,27 @@ export class DecryptNotesTask extends WorkerTask { jobId, }: DecryptNotesRequest): DecryptNotesResponse { const decryptedNotes = [] + const incomingViewKeys = accountKeys.map(({ incomingViewKey }) => incomingViewKey) + const noteOptions = { + skipValidation: options.skipNoteValidation, + } for (const { serializedNote, currentNoteIndex } of encryptedNotes) { - const note = new NoteEncrypted(serializedNote, { - skipValidation: options.skipNoteValidation, - }) - - const receivedNotes = note.decryptNoteForOwners( - accountKeys.map(({ incomingViewKey }) => incomingViewKey), - ) + const note = new NoteEncrypted(serializedNote, noteOptions) + const receivedNotes = note.decryptNoteForOwners(incomingViewKeys) for (const [i, receivedNote] of receivedNotes.entries()) { // Try decrypting the note as the owner if (receivedNote && receivedNote.value() !== 0n) { - const { viewKey } = accountKeys[i] + const key = accountKeys.at(i) + Assert.isNotUndefined(key) decryptedNotes.push({ index: currentNoteIndex, forSpender: false, hash: note.hash(), nullifier: currentNoteIndex !== null - ? receivedNote.nullifier(viewKey, BigInt(currentNoteIndex)) + ? receivedNote.nullifier(key.viewKey.toString('hex'), BigInt(currentNoteIndex)) : null, serializedNote: receivedNote.serialize(), }) @@ -308,8 +415,9 @@ export class DecryptNotesTask extends WorkerTask { if (options.decryptForSpender) { // Try decrypting the note as the spender - const { outgoingViewKey } = accountKeys[i] - const spentNote = note.decryptNoteForSpender(outgoingViewKey) + const key = accountKeys.at(i) + Assert.isNotUndefined(key) + const spentNote = note.decryptNoteForSpender(key.outgoingViewKey) if (spentNote && spentNote.value() !== 0n) { decryptedNotes.push({ index: currentNoteIndex, diff --git a/ironfish/src/workerPool/tasks/workerMessage.ts b/ironfish/src/workerPool/tasks/workerMessage.ts index 6679eb7b76..da6da961a5 100644 --- a/ironfish/src/workerPool/tasks/workerMessage.ts +++ b/ironfish/src/workerPool/tasks/workerMessage.ts @@ -3,6 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import bufio from 'bufio' +import { MessagePort, Worker as WorkerThread } from 'worker_threads' import { Serializable } from '../../common/serializable' import { WorkerHeader } from '../interfaces/workerHeader' @@ -34,6 +35,10 @@ export abstract class WorkerMessage implements Serializable { abstract serializePayload(bw: bufio.StaticWriter | bufio.BufferWriter): void abstract getSize(): number + getSharedMemoryPayload(): SharedArrayBuffer | null { + return null + } + static deserializeHeader(buffer: Buffer): WorkerHeader { const br = bufio.read(buffer) const jobId = Number(br.readU64()) @@ -46,6 +51,20 @@ export abstract class WorkerMessage implements Serializable { } } + /** + * Serializes the contents of this message into a `Buffer` to be sent to a + * worker. + * + * Note that the `Buffer` will be *transferred* to the destination worker. + * This means that the buffer will become inutilizable from the sending + * context once sent. For this reason, it's important that the returned + * `Buffer` does not reference any memory that will be needed later by the + * sending context. An easy way to ensure that is to create a new `Buffer` + * every time this method is called. + * + * See https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage + * for more details on transfers. + */ serialize(): Buffer { const bw = bufio.write(WORKER_MESSAGE_HEADER_SIZE + this.getSize()) bw.writeU64(this.jobId) @@ -53,4 +72,10 @@ export abstract class WorkerMessage implements Serializable { this.serializePayload(bw) return bw.render() } + + post(thread: WorkerThread | MessagePort) { + const body = this.serialize().buffer + const sharedData = this.getSharedMemoryPayload() + thread.postMessage([body, sharedData], [body]) + } } diff --git a/ironfish/src/workerPool/tasks/workerMessages.test.perf.ts b/ironfish/src/workerPool/tasks/workerMessages.test.perf.ts index eae7d4baba..3cb5f1f8f2 100644 --- a/ironfish/src/workerPool/tasks/workerMessages.test.perf.ts +++ b/ironfish/src/workerPool/tasks/workerMessages.test.perf.ts @@ -68,7 +68,11 @@ describe('WorkerMessages', () => { const { block, transactions } = await useBlockWithTxs(nodeTest.node, txCount, account) await expect(nodeTest.chain).toAddBlock(block) - const accountKeys: DecryptNotesAccountKey = { ...account } + const accountKeys: DecryptNotesAccountKey = { + incomingViewKey: Buffer.from(account.incomingViewKey, 'hex'), + outgoingViewKey: Buffer.from(account.outgoingViewKey, 'hex'), + viewKey: Buffer.from(account.viewKey, 'hex'), + } const items: DecryptNotesItem[] = [] for (const transaction of transactions) { diff --git a/ironfish/src/workerPool/worker.ts b/ironfish/src/workerPool/worker.ts index 6be11c815a..13313eaf90 100644 --- a/ironfish/src/workerPool/worker.ts +++ b/ironfish/src/workerPool/worker.ts @@ -61,9 +61,9 @@ export class Worker { send(message: WorkerMessage): void { if (this.thread) { - this.thread.postMessage(message.serialize()) + message.post(this.thread) } else if (this.parent) { - this.parent.postMessage(message.serialize()) + message.post(this.parent) } else { throw new Error(`Cannot send message: no thread or worker`) } @@ -115,7 +115,10 @@ export class Worker { initializeSapling() } - private onMessageFromParent = (request: Uint8Array): void => { + private onMessageFromParent = ([request, sharedMemory]: [ + request: Uint8Array, + sharedMemory: SharedArrayBuffer | null, + ]): void => { const message = Buffer.from(request) let header: WorkerHeader @@ -130,7 +133,7 @@ export class Worker { let requestBody: WorkerMessage try { - requestBody = this.parseRequest(jobId, type, body) + requestBody = this.parseRequest(jobId, type, body, sharedMemory) } catch { const args = `(jobId: ${jobId}, type: ${WorkerMessageType[type]}, body: '${body.toString( 'hex', @@ -165,7 +168,10 @@ export class Worker { }) } - private onMessageFromWorker = (response: Uint8Array): void => { + private onMessageFromWorker = ([response, _sharedMemory]: [ + response: Uint8Array, + sharedMemory: SharedArrayBuffer | null, + ]): void => { const message = Buffer.from(response) let header: WorkerHeader @@ -214,14 +220,19 @@ export class Worker { return } - private parseRequest(jobId: number, type: WorkerMessageType, request: Buffer): WorkerMessage { + private parseRequest( + jobId: number, + type: WorkerMessageType, + request: Buffer, + sharedMemory: SharedArrayBuffer | null, + ): WorkerMessage { switch (type) { case WorkerMessageType.CreateMinersFee: return CreateMinersFeeRequest.deserializePayload(jobId, request) case WorkerMessageType.PostTransaction: return PostTransactionRequest.deserializePayload(jobId, request) case WorkerMessageType.DecryptNotes: - return DecryptNotesRequest.deserializePayload(jobId, request) + return DecryptNotesRequest.deserializePayload(jobId, request, sharedMemory) case WorkerMessageType.JobAborted: return JobAbortedMessage.deserializePayload() case WorkerMessageType.JobError: From dad63bad7d318da02e863e0bb525d53f99cdea7d Mon Sep 17 00:00:00 2001 From: andrea Date: Wed, 10 Jul 2024 12:10:06 -0700 Subject: [PATCH 45/81] Use a sparse array in `DecryptNotesResponse` Most of the note decryption will fail. Currently, if a `DecryptNotesResponse` contains 100 undecrypted notes, it will create a dense array containing 100 `null` values, causing unnecessary large memory allocations and large memory writes. It was in fact observed that the arrays from `DecryptNotesResponse` were one of the top contributors to memory usage during wallet scans. This commit changes the implementation so that now `DecryptNotesResponse` would create a sparse array containing no values at all. This means that, when no notes are decrypted (i.e. the most common case), all `DecryptNotesResponse` objects consume the same amount of memory, regardless of how many accounts are in the wallet, or how many encrypted notes were requested. This was shown to reduce the peak memory usage of the node process, as well as reduce the number of minor page faults. --- ironfish/src/rpc/routes/wallet/utils.ts | 2 +- ironfish/src/wallet/scanner/noteDecryptor.ts | 7 +- ironfish/src/wallet/wallet.ts | 8 +- ironfish/src/workerPool/pool.ts | 2 +- .../src/workerPool/tasks/decryptNotes.test.ts | 38 +++++++- ironfish/src/workerPool/tasks/decryptNotes.ts | 92 +++++++++++-------- 6 files changed, 102 insertions(+), 47 deletions(-) diff --git a/ironfish/src/rpc/routes/wallet/utils.ts b/ironfish/src/rpc/routes/wallet/utils.ts index a236bac21c..4e7b343d83 100644 --- a/ironfish/src/rpc/routes/wallet/utils.ts +++ b/ironfish/src/rpc/routes/wallet/utils.ts @@ -80,7 +80,7 @@ export async function getTransactionNotes( Assert.isNotUndefined(decryptedSends) for (const note of decryptedSends) { - if (note === null) { + if (note === undefined) { continue } diff --git a/ironfish/src/wallet/scanner/noteDecryptor.ts b/ironfish/src/wallet/scanner/noteDecryptor.ts index bc409c48b2..924bbef172 100644 --- a/ironfish/src/wallet/scanner/noteDecryptor.ts +++ b/ironfish/src/wallet/scanner/noteDecryptor.ts @@ -239,7 +239,7 @@ export class BackgroundNoteDecryptor { function* regroupNotes( accounts: ReadonlyArray, transactions: ReadonlyArray, - decryptedNotes: ReadonlyMap>, + decryptedNotes: ReadonlyMap>, ): Generator<{ account: Account decryptedTransactions: Array<{ @@ -249,7 +249,8 @@ function* regroupNotes( }> { for (const account of accounts) { let notesOffset = 0 - const flatNotes: ReadonlyArray = decryptedNotes.get(account.id) ?? [] + const flatNotes: ReadonlyArray = + decryptedNotes.get(account.id) ?? [] const groupedNotes: Array<{ transaction: Transaction decryptedNotes: Array @@ -258,7 +259,7 @@ function* regroupNotes( for (const transaction of transactions) { const decryptedNotes = flatNotes .slice(notesOffset, notesOffset + transaction.notes.length) - .filter((note) => note !== null) as Array + .filter((note) => note !== undefined) as Array groupedNotes.push({ transaction, decryptedNotes }) notesOffset += transaction.notes.length } diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index ecf96dc84e..cb5215c514 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.ts @@ -353,7 +353,7 @@ export class Wallet { accounts: ReadonlyArray, ): Promise>> { const workloadSize = 20 - const notePromises: Array>>> = [] + const notePromises: Array>>> = [] let decryptNotesPayloads = [] let currentNoteIndex = initialNoteIndex @@ -388,7 +388,9 @@ export class Wallet { for (const [accountId, decryptedNotes] of partialResult.entries()) { const list = mergedResults.get(accountId) Assert.isNotUndefined(list) - list.push(...(decryptedNotes.filter((note) => note !== null) as Array)) + list.push( + ...(decryptedNotes.filter((note) => note !== undefined) as Array), + ) } } @@ -398,7 +400,7 @@ export class Wallet { private decryptNotesFromTransaction( accounts: ReadonlyArray, encryptedNotes: Array, - ): Promise>> { + ): Promise>> { const accountKeys = accounts.map((account) => ({ accountId: account.id, incomingViewKey: Buffer.from(account.incomingViewKey, 'hex'), diff --git a/ironfish/src/workerPool/pool.ts b/ironfish/src/workerPool/pool.ts index 197f37194a..0487f26b3e 100644 --- a/ironfish/src/workerPool/pool.ts +++ b/ironfish/src/workerPool/pool.ts @@ -196,7 +196,7 @@ export class WorkerPool { accountKeys: ReadonlyArray<{ accountId: string } & DecryptNotesAccountKey>, encryptedNotes: ReadonlyArray, options: DecryptNotesOptions, - ): Promise>> { + ): Promise>> { const request = new DecryptNotesRequest(accountKeys, encryptedNotes, options) const response = await this.execute(request).result() diff --git a/ironfish/src/workerPool/tasks/decryptNotes.test.ts b/ironfish/src/workerPool/tasks/decryptNotes.test.ts index 96a9e47cea..bf7404a44f 100644 --- a/ironfish/src/workerPool/tasks/decryptNotes.test.ts +++ b/ironfish/src/workerPool/tasks/decryptNotes.test.ts @@ -2,6 +2,7 @@ * 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 { DECRYPTED_NOTE_LENGTH, ENCRYPTED_NOTE_LENGTH } from '@ironfish/rust-nodejs' +import { Assert } from '../../assert' import { createNodeTest, serializePayloadToBuffer, @@ -13,6 +14,7 @@ import { import { ACCOUNT_KEY_LENGTH } from '../../wallet' import { VIEW_KEY_LENGTH } from '../../wallet/walletdb/accountValue' import { + DecryptedNote, DecryptNotesRequest, DecryptNotesResponse, DecryptNotesSharedAccountKeys, @@ -121,7 +123,7 @@ describe('DecryptNotesResponse', () => { nullifier: Buffer.alloc(32, 1), serializedNote: Buffer.alloc(DECRYPTED_NOTE_LENGTH, 1), }, - null, + undefined, ], 0, ) @@ -148,6 +150,38 @@ describe('DecryptNotesResponse', () => { expect(deserializedResponse.notes).toHaveLength(length) }) + it('uses sparses arrays to minimize memory usage', () => { + const notes = [] + const notesLength = 10000 + const testNote = { + forSpender: false, + index: 1, + hash: Buffer.alloc(32, 1), + nullifier: Buffer.alloc(32, 1), + serializedNote: Buffer.alloc(DECRYPTED_NOTE_LENGTH, 1), + } + notes[1000] = testNote + notes[2000] = testNote + notes[3000] = testNote + notes.length = notesLength + expect(notes).toHaveLength(notesLength) + + const response = new DecryptNotesResponse(notes, 0) + const buffer = serializePayloadToBuffer(response) + const deserializedResponse = DecryptNotesResponse.deserializePayload(response.jobId, buffer) + + expect(deserializedResponse.notes).toHaveLength(notesLength) + expect(deserializedResponse.notes).toEqual(notes) + + const explicitlySetNotes = new Array() + deserializedResponse.notes.forEach((note) => { + Assert.isNotUndefined(note) + explicitlySetNotes.push(note) + }) + expect(explicitlySetNotes).toHaveLength(3) + expect(explicitlySetNotes).toEqual([testNote, testNote, testNote]) + }) + describe('mapToAccounts', () => { it('returns a map linking each account to its notes', () => { const accounts = 'abcdefghijklmnopqrstuvwxyz' @@ -283,7 +317,7 @@ describe('DecryptNotesTask', () => { ) const responseNoSpender = task.execute(requestNoSpender) - expect(responseNoSpender).toMatchObject({ notes: [null] }) + expect(responseNoSpender).toMatchObject({ notes: [undefined] }) }) }) }) diff --git a/ironfish/src/workerPool/tasks/decryptNotes.ts b/ironfish/src/workerPool/tasks/decryptNotes.ts index d6277c07de..23ad380718 100644 --- a/ironfish/src/workerPool/tasks/decryptNotes.ts +++ b/ironfish/src/workerPool/tasks/decryptNotes.ts @@ -229,9 +229,9 @@ export class DecryptNotesRequest extends WorkerMessage { } export class DecryptNotesResponse extends WorkerMessage { - readonly notes: Array + readonly notes: Array - constructor(notes: Array, jobId: number) { + constructor(notes: Array, jobId: number) { super(WorkerMessageType.DecryptNotes, jobId) this.notes = notes } @@ -258,12 +258,15 @@ export class DecryptNotesResponse extends WorkerMessage { bw.writeU32(this.notes.length) - for (const [arrayIndex, note] of this.notes.entries()) { + // `this.notes` may be a sparse array. Using `forEach()` will skip the + // unset slots (as opposed to using `for (... of this.notes)`, which will + // iterate over the unset slots). + this.notes.forEach((note, notesArrayIndex) => { if (!note) { - continue + return } - bw.writeU32(arrayIndex) + bw.writeU32(notesArrayIndex) let flags = 0 flags |= Number(!!note.index) << 0 @@ -280,17 +283,17 @@ export class DecryptNotesResponse extends WorkerMessage { if (note.nullifier) { bw.writeHash(note.nullifier) } - } + }) } static deserializePayload(jobId: number, buffer: Buffer): DecryptNotesResponse { const reader = bufio.read(buffer) - const arrayLength = reader.readU32() - const notes = Array(arrayLength).fill(null) as Array + const notes = new Array() + notes.length = reader.readU32() while (reader.left() > 0) { - const arrayIndex = reader.readU32() + const notesArrayIndex = reader.readU32() const flags = reader.readU8() const hasIndex = flags & (1 << 0) @@ -309,7 +312,7 @@ export class DecryptNotesResponse extends WorkerMessage { nullifier = reader.readHash() } - notes[arrayIndex] = { + notes[notesArrayIndex] = { forSpender, index, hash, @@ -322,11 +325,11 @@ export class DecryptNotesResponse extends WorkerMessage { } getSize(): number { - let size = 4 - - for (const note of this.notes) { + // `this.notes` may be a sparse array. `reduce()` won't visit the unset + // slots in that case. + return this.notes.reduce((size, note) => { if (!note) { - continue + return size } size += 4 + 1 + 32 + DECRYPTED_NOTE_LENGTH @@ -338,9 +341,9 @@ export class DecryptNotesResponse extends WorkerMessage { if (note.nullifier) { size += 32 } - } - return size + return size + }, 4) } /** @@ -350,21 +353,33 @@ export class DecryptNotesResponse extends WorkerMessage { */ mapToAccounts( accounts: ReadonlyArray<{ accountId: string }>, - ): Map> { - const decryptedNotesByAccount: Array< - [accountId: string, notes: Array] - > = accounts.map(({ accountId }) => [accountId, []]) - - let noteIndex = 0 - while (noteIndex < this.notes.length) { - for (const [_, accountNotes] of decryptedNotesByAccount) { - const nextNote: DecryptedNote | null | undefined = this.notes[noteIndex++] - Assert.isNotUndefined(nextNote) - accountNotes.push(nextNote) - } + ): Map> { + if ( + !(this.notes.length === 0 && accounts.length === 0) && + this.notes.length % accounts.length !== 0 + ) { + throw new Error( + `${this.notes.length} notes cannot be mapped to ${accounts.length} accounts`, + ) } - Assert.isEqual(noteIndex, this.notes.length) + const notesPerAccount = Math.trunc(this.notes.length / accounts.length) + + const decryptedNotesByAccount: Array< + [accountId: string, notes: Array] + > = accounts.map(({ accountId }) => { + const accountNotes: Array = [] + accountNotes.length = notesPerAccount + return [accountId, accountNotes] + }) + + this.notes.forEach((note, notesArrayIndex) => { + const accountIndex = notesArrayIndex % accounts.length + const accountNotesArrayIndex = Math.trunc(notesArrayIndex / accounts.length) + const [_accountId, accountNotes] = decryptedNotesByAccount[accountIndex] + accountNotes[accountNotesArrayIndex] = note + }) + return new Map(decryptedNotesByAccount) } } @@ -391,16 +406,19 @@ export class DecryptNotesTask extends WorkerTask { skipValidation: options.skipNoteValidation, } + decryptedNotes.length = incomingViewKeys.length * encryptedNotes.length + + let decryptedNoteIndex = 0 for (const { serializedNote, currentNoteIndex } of encryptedNotes) { const note = new NoteEncrypted(serializedNote, noteOptions) const receivedNotes = note.decryptNoteForOwners(incomingViewKeys) - for (const [i, receivedNote] of receivedNotes.entries()) { + for (const [accountIndex, receivedNote] of receivedNotes.entries()) { // Try decrypting the note as the owner if (receivedNote && receivedNote.value() !== 0n) { - const key = accountKeys.at(i) + const key = accountKeys.at(accountIndex) Assert.isNotUndefined(key) - decryptedNotes.push({ + decryptedNotes[decryptedNoteIndex++] = { index: currentNoteIndex, forSpender: false, hash: note.hash(), @@ -409,28 +427,28 @@ export class DecryptNotesTask extends WorkerTask { ? receivedNote.nullifier(key.viewKey.toString('hex'), BigInt(currentNoteIndex)) : null, serializedNote: receivedNote.serialize(), - }) + } continue } if (options.decryptForSpender) { // Try decrypting the note as the spender - const key = accountKeys.at(i) + const key = accountKeys.at(accountIndex) Assert.isNotUndefined(key) const spentNote = note.decryptNoteForSpender(key.outgoingViewKey) if (spentNote && spentNote.value() !== 0n) { - decryptedNotes.push({ + decryptedNotes[decryptedNoteIndex++] = { index: currentNoteIndex, forSpender: true, hash: note.hash(), nullifier: null, serializedNote: spentNote.serialize(), - }) + } continue } } - decryptedNotes.push(null) + decryptedNoteIndex++ } } From 6207960e6e354127fb2e4cd9d7d9d712025babfa Mon Sep 17 00:00:00 2001 From: andrea Date: Thu, 11 Jul 2024 17:28:23 -0700 Subject: [PATCH 46/81] Optimize `WalletDB.loadNoteHash()` to avoid hitting the database if the note nullifier is known to be absent This commit adds an in-memory bloom filter to remember which nullifiers are stored in the database. The bloom filter is used by `WalletDB.loadNoteHash()` to avoid querying the database if the given nullifier is known to be not present. While at it, `WalletDB.getTransactionHashFromNullifier()` was optimized in the same way. --- ironfish/src/utils/bloomFilter.test.ts | 53 +++++++++++++ ironfish/src/utils/bloomFilter.ts | 77 +++++++++++++++++++ .../__fixtures__/walletdb.test.ts.fixture | 60 +++++++++++++++ ironfish/src/wallet/walletdb/walletdb.test.ts | 46 +++++++++++ ironfish/src/wallet/walletdb/walletdb.ts | 54 ++++++++++++- 5 files changed, 287 insertions(+), 3 deletions(-) create mode 100644 ironfish/src/utils/bloomFilter.test.ts create mode 100644 ironfish/src/utils/bloomFilter.ts diff --git a/ironfish/src/utils/bloomFilter.test.ts b/ironfish/src/utils/bloomFilter.test.ts new file mode 100644 index 0000000000..c7c7067b0d --- /dev/null +++ b/ironfish/src/utils/bloomFilter.test.ts @@ -0,0 +1,53 @@ +/* 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 { randomBytes } from 'crypto' +import { BloomFilter } from './bloomFilter' + +describe('BloomFilter', () => { + describe('maybeHas', () => { + it('always returns false when empty', () => { + const filter = new BloomFilter(256) + + for (let i = 0; i < 1000; i++) { + const item = randomBytes(32) + expect(filter.maybeHas(item)).toBe(false) + } + }) + + it('returns true for all items that were explicitly put', () => { + const filter = new BloomFilter(256) + + for (let i = 0; i < 1000; i++) { + const item = randomBytes(32) + filter.put(item) + expect(filter.maybeHas(item)).toBe(true) + } + }) + + it('returns true for items that were not explicitly but, but have the same prefix as previously put items', () => { + const filter = new BloomFilter(512) + + const samePrefix = [ + Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]), + Buffer.from([1, 2, 3, 4, 9, 10, 11, 12]), + Buffer.from([1, 2, 3, 4, 13, 14, 15, 16]), + ] + const differentPrefix = [ + Buffer.from([1, 1, 1, 1, 1, 1, 1, 1]), + Buffer.from([2, 2, 2, 2, 2, 2, 2, 2]), + Buffer.from([3, 3, 3, 3, 3, 3, 3, 3]), + ] + + filter.put(samePrefix[0]) + + for (const item of samePrefix) { + expect(filter.maybeHas(item)).toBe(true) + } + for (const item of differentPrefix) { + expect(filter.maybeHas(item)).toBe(false) + } + }) + }) +}) diff --git a/ironfish/src/utils/bloomFilter.ts b/ironfish/src/utils/bloomFilter.ts new file mode 100644 index 0000000000..aa7ee583dc --- /dev/null +++ b/ironfish/src/utils/bloomFilter.ts @@ -0,0 +1,77 @@ +/* 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/. */ + +function isPowerOf2(n: number): boolean { + // This checks that n is non-zero, and that n and n-1 have no bits in common + // (taken from https://stackoverflow.com/a/30924333) + return !!(n && !(n & (n - 1))) +} + +/** + * Simple implementation of a bloom filter. This is a set-like data structure + * that can be used to check whether certain items may or may not be present in + * the set. This is a probabilistic data structure that does not return false + * negatives, but may return false positives. + * + * Usually, bloom filters work by computing the hash of each item added to the + * set. This implementation differs in that it does not compute any hash for + * efficiency, and instead assumes that each item contains random data. Items + * that share the same prefix will result in collisions/false positives. + */ +export class BloomFilter { + private readonly bitMask: number + private readonly bitArray: Buffer + + constructor(bits: number) { + if (bits < 8) { + // Need to have at least 1 byte + throw new Error(`bits must be at least 8 (got ${bits})`) + } + if (bits >= 1 << 30) { + // `bits` cannot exceed 2**30 because `indexOf` relies on + // `Buffer.readUInt32LE` (also, the array would be larger than 128 MiB, + // which may not be desiderable). This is 30 and not 32 because `1 << 32 + // === 1` and `1 << 31 === -2147483648` + throw new Error(`bits cannot exceed 2**30 (got ${bits})`) + } + if (!isPowerOf2(bits)) { + // Having `bits` as a power of 2 means that `bytes` (calculated below) + // will be a power of 2, and this allows efficient items lookup without + // using any modulo operator + throw new Error(`bits must be a power of 2`) + } + const bytes = bits >> 3 + this.bitArray = Buffer.alloc(bytes) + this.bitMask = bits - 1 + } + + private indexOf(item: Buffer): [byte: number, bit: number] { + // One of the assumption for this BloomFilter is that the items contain + // random data, so that we can skip hashing it. With that assumption in + // mind, we take the first `bits` out of `item` and use that as the index + // in the array. + const index = item.readUInt32LE(0) & this.bitMask + const byte = index >> 3 + const bit = index & 0b111 + return [byte, bit] + } + + /** + * Adds the item to the set. + */ + put(item: Buffer) { + const [byte, bit] = this.indexOf(item) + this.bitArray[byte] |= 1 << bit + } + + /** + * Returns true if the item *may* have been previously added by a call to + * `put()`, false otherwise. This method may return false positives, but + * never returns false negatives. + */ + maybeHas(item: Buffer): boolean { + const [byte, bit] = this.indexOf(item) + return !!(this.bitArray[byte] & (1 << bit)) + } +} diff --git a/ironfish/src/wallet/walletdb/__fixtures__/walletdb.test.ts.fixture b/ironfish/src/wallet/walletdb/__fixtures__/walletdb.test.ts.fixture index efb0e518aa..36fc19d4c0 100644 --- a/ironfish/src/wallet/walletdb/__fixtures__/walletdb.test.ts.fixture +++ b/ironfish/src/wallet/walletdb/__fixtures__/walletdb.test.ts.fixture @@ -730,5 +730,65 @@ } ] } + ], + "WalletDB loadNoteHash loads the hash of a note given its nullifier": [ + { + "value": { + "version": 4, + "id": "16812363-20b7-4d3b-a5f1-d63f9c500e9e", + "name": "test", + "spendingKey": "42c4d417d5a6da2900cbfc8a32bfbedd8c96e1848dc4a22ec6b9386fd30d7f36", + "viewKey": "d220f7afded99beec10b955b4cd9e193f59a9b0cc3a6f0d5d284f606a442cbc52f1fdbfe00b35666fde4b01bddfb5179988d1b521ee318ebe4e7914e0aa080b7", + "incomingViewKey": "ff5905b8bd1238d6e7b48082ec38caa20b3481e4a3cd0ddcbdf4d071b347a406", + "outgoingViewKey": "805590598f08f2f6c3994315b8da4d0810049f010b33c46b5928487357f86250", + "publicAddress": "0a26afc3a56a3cbccd3eae6784885776df78f406235edcbd622feef724329220", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "6a7b7fecfdb8b21f5521e323d7867fd556bdfaffc93679527f099e0118014d0d" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + } + ], + "WalletDB loadNoteHash avoids hitting the database if the nullifier is known to be not present": [ + { + "value": { + "version": 4, + "id": "de2f768a-27d8-42b1-b23f-50423f469bbb", + "name": "test", + "spendingKey": "c3c9ba31682cab87d2388ad28e3c8a8e1141e802ab69beb1d2024dab7371d95c", + "viewKey": "38bfce8064573739fbe71f220bd1b055d85fa928bb12cab6ea6cec39228524c49011b98abac356d07ce11a9073d5257935459935c704f49189de32f7450cf7d4", + "incomingViewKey": "9ae8ad607e7429180f9c9b5c00c405945d0ece39ebc0bfb7298b7635ce2d9e06", + "outgoingViewKey": "d682f910ae6d4d1f55de2def975d4db0f66f9289fbdb503cd4a12672fdd03063", + "publicAddress": "89380c90a6c6829f7302e919932fa1b8f50c38c92f30f7313f4ce425062ebecc", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "fd03d976919b66a79480321fb7a04076897c7feb5180df93089d78e39503b90a" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + } ] } \ No newline at end of file diff --git a/ironfish/src/wallet/walletdb/walletdb.test.ts b/ironfish/src/wallet/walletdb/walletdb.test.ts index 2d3f007cc6..90dc863fe7 100644 --- a/ironfish/src/wallet/walletdb/walletdb.test.ts +++ b/ironfish/src/wallet/walletdb/walletdb.test.ts @@ -2,6 +2,7 @@ * 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, multisig } from '@ironfish/rust-nodejs' +import { randomBytes } from 'crypto' import { Assert } from '../../assert' import { createNodeTest, @@ -357,6 +358,51 @@ describe('WalletDB', () => { }) }) + describe('loadNoteHash', () => { + it('loads the hash of a note given its nullifier', async () => { + const node = (await nodeTest.createSetup()).node + const walletDb = node.wallet.walletDb + const account = await useAccountFixture(node.wallet) + + const noteHash = randomBytes(32) + const nullifier = randomBytes(32) + + await expect(walletDb.loadNoteHash(account, nullifier)).resolves.toBeUndefined() + await walletDb.saveNullifierNoteHash(account, nullifier, noteHash) + await expect(walletDb.loadNoteHash(account, nullifier)).resolves.toEqual(noteHash) + }) + + it('avoids hitting the database if the nullifier is known to be not present', async () => { + const node = (await nodeTest.createSetup()).node + const walletDb = node.wallet.walletDb + const account = await useAccountFixture(node.wallet) + + const noteHash = randomBytes(32) + // Randomly generated nullifiers. Using a static string instead of + // randomBytes() because loadNoteHash uses a bloom filter, which is a + // probabilistic data structure. Using a random value for each test run + // would make the test flaky. + const nullifier = Buffer.from( + 'efc9eaadc6668b24900994670acad2010b497cbefdf7a27f5f463eba59b3ed14', + 'hex', + ) + const absentNullifier = Buffer.from( + '820fabbe98bc7a7054700861949b83cfaeb00f3b8ab4028299be7315a4c7b551', + 'hex', + ) + + await walletDb.saveNullifierNoteHash(account, nullifier, noteHash) + + const getSpy = jest.spyOn(walletDb.nullifierToNoteHash, 'get') + await expect(walletDb.loadNoteHash(account, nullifier)).resolves.toEqual(noteHash) + expect(getSpy).toHaveBeenCalled() + + getSpy.mockClear() + await expect(walletDb.loadNoteHash(account, absentNullifier)).resolves.toBeUndefined() + expect(getSpy).not.toHaveBeenCalled() + }) + }) + describe('loadTransactions', () => { it('loads transactions within a given key range', async () => { const node = (await nodeTest.createSetup()).node diff --git a/ironfish/src/wallet/walletdb/walletdb.ts b/ironfish/src/wallet/walletdb/walletdb.ts index 9853f521af..d1305dd84a 100644 --- a/ironfish/src/wallet/walletdb/walletdb.ts +++ b/ironfish/src/wallet/walletdb/walletdb.ts @@ -29,6 +29,7 @@ import { import { getPrefixesKeyRange, StorageUtils } from '../../storage/database/utils' import { createDB } from '../../storage/utils' import { BufferUtils } from '../../utils' +import { BloomFilter } from '../../utils/bloomFilter' import { WorkerPool } from '../../workerPool' import { Account, calculateAccountPrefix } from '../account/account' import { AccountValue, AccountValueEncoding } from './accountValue' @@ -147,6 +148,8 @@ export class WalletDB { cacheStores: Array> + nullifierBloomFilter: BloomFilter | null = null + constructor({ files, location, @@ -740,12 +743,40 @@ export class WalletDB { } } + private loadNullifierBloomFilter(tx?: IDatabaseTransaction): Promise { + if (this.nullifierBloomFilter) { + return Promise.resolve(this.nullifierBloomFilter) + } else { + return this.db.withTransaction(tx, async (tx) => { + if (this.nullifierBloomFilter) { + // A concurrent call to this function already created the filter + return this.nullifierBloomFilter + } + + const nullifierBloomFilter = new BloomFilter(0x800000) // 1 MiB bloom filter + for await (const [_accountPrefix, nullifier] of this.nullifierToNoteHash.getAllKeysIter( + tx, + )) { + nullifierBloomFilter.put(nullifier) + } + + this.nullifierBloomFilter = nullifierBloomFilter + return nullifierBloomFilter + }) + } + } + async loadNoteHash( account: Account, nullifier: Buffer, tx?: IDatabaseTransaction, ): Promise { - return this.nullifierToNoteHash.get([account.prefix, nullifier], tx) + const nullifierFilter = await this.loadNullifierBloomFilter(tx) + if (nullifierFilter.maybeHas(nullifier)) { + return this.nullifierToNoteHash.get([account.prefix, nullifier], tx) + } else { + return undefined + } } async saveNullifierNoteHash( @@ -755,6 +786,9 @@ export class WalletDB { tx?: IDatabaseTransaction, ): Promise { await this.nullifierToNoteHash.put([account.prefix, nullifier], noteHash, tx) + if (this.nullifierBloomFilter) { + this.nullifierBloomFilter.put(nullifier) + } } async *loadNullifierToNoteHash( @@ -780,6 +814,9 @@ export class WalletDB { nullifier: Buffer, tx?: IDatabaseTransaction, ): Promise { + // `nullifierBloomFilter` is unchanged after calling this method. The + // assumption is that this method is called rarely, and so special logic to + // "reset" the bloom filter is not deemed necessary. await this.nullifierToNoteHash.del([account.prefix, nullifier], tx) } @@ -791,7 +828,7 @@ export class WalletDB { ): Promise { await this.db.withTransaction(tx, async (tx) => { if (note.nullifier) { - await this.nullifierToNoteHash.put([account.prefix, note.nullifier], noteHash, tx) + await this.saveNullifierNoteHash(account, note.nullifier, noteHash, tx) } await this.setNoteHashSequence(account, noteHash, note.sequence, tx) @@ -1229,7 +1266,12 @@ export class WalletDB { nullifier: Buffer, tx?: IDatabaseTransaction, ): Promise { - return this.nullifierToTransactionHash.get([account.prefix, nullifier], tx) + const nullifierFilter = await this.loadNullifierBloomFilter(tx) + if (nullifierFilter.maybeHas(nullifier)) { + return this.nullifierToTransactionHash.get([account.prefix, nullifier], tx) + } else { + return undefined + } } async saveNullifierToTransactionHash( @@ -1243,6 +1285,9 @@ export class WalletDB { transaction.hash(), tx, ) + if (this.nullifierBloomFilter) { + this.nullifierBloomFilter.put(nullifier) + } } async deleteNullifierToTransactionHash( @@ -1250,6 +1295,9 @@ export class WalletDB { nullifier: Buffer, tx?: IDatabaseTransaction, ): Promise { + // `nullifierBloomFilter` is unchanged after calling this method. The + // assumption is that this method is called rarely, and so special logic to + // "reset" the bloom filter is not deemed necessary. await this.nullifierToTransactionHash.del([account.prefix, nullifier], tx) } From fbb0cfeefc3adae6351d7fd541316e10cae14e4f Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Wed, 17 Jul 2024 11:00:32 -0700 Subject: [PATCH 47/81] Renders transaction summary at the end of the sign command. (#5129) * Renders transaction summary at the end of the sign command. * lint --- ironfish-cli/src/commands/wallet/sign.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ironfish-cli/src/commands/wallet/sign.ts b/ironfish-cli/src/commands/wallet/sign.ts index 420fbbbc66..354ece2177 100644 --- a/ironfish-cli/src/commands/wallet/sign.ts +++ b/ironfish-cli/src/commands/wallet/sign.ts @@ -8,7 +8,7 @@ import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' import { longPrompt } from '../../utils/input' import { Ledger } from '../../utils/ledger' -import { watchTransaction } from '../../utils/transaction' +import { renderTransactionDetails, watchTransaction } from '../../utils/transaction' export class SignTransaction extends IronfishCommand { static description = `Sign an unsigned transaction` @@ -73,6 +73,8 @@ export class SignTransaction extends IronfishCommand { this.log(`\nHash: ${transaction.hash().toString('hex')}`) this.log(`Fee: ${CurrencyUtils.render(transaction.fee(), true)}`) + await renderTransactionDetails(client, transaction, account, this.logger) + if (flags.broadcast && response.content.accepted === false) { this.error( `Transaction '${transaction.hash().toString('hex')}' was not accepted into the mempool`, From 8f62bbeb8aa9c42fb38c521fa925d6d740be9973 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Thu, 18 Jul 2024 13:58:28 -0700 Subject: [PATCH 48/81] Remove chain:show command (#5145) This is no longer used since testnet and isn't in line with what we want to support anyway. --- ironfish-cli/src/commands/chain/show.ts | 38 ------------------------- 1 file changed, 38 deletions(-) delete mode 100644 ironfish-cli/src/commands/chain/show.ts diff --git a/ironfish-cli/src/commands/chain/show.ts b/ironfish-cli/src/commands/chain/show.ts deleted file mode 100644 index a91ab9ccf4..0000000000 --- a/ironfish-cli/src/commands/chain/show.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* 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 { Args } from '@oclif/core' -import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' - -export default class Show extends IronfishCommand { - static description = 'Show the heaviest chain' - - static flags = { - ...LocalFlags, - } - - static args = { - start: Args.integer({ - default: -50, - required: false, - description: 'The sequence to start at (inclusive, genesis block is 1)', - }), - stop: Args.integer({ - required: false, - description: 'The sequence to end at (inclusive)', - }), - } - - async start(): Promise { - const { args } = await this.parse(Show) - const { start, stop } = args - - this.log(`Getting the chain blocks...`) - await this.sdk.client.connect() - - const data = await this.sdk.client.chain.showChain({ start, stop }) - - data.content.content.forEach((content) => this.log(content)) - } -} From 12bc990ef3cd12d418a7c9a12fccdbf82d9de79a Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Thu, 18 Jul 2024 13:58:43 -0700 Subject: [PATCH 49/81] Make chain:rewind command hidden (#5144) This is part of the CLI usability audit. --- ironfish-cli/src/commands/chain/rewind.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ironfish-cli/src/commands/chain/rewind.ts b/ironfish-cli/src/commands/chain/rewind.ts index 886bfa8bcf..fb0a0ef150 100644 --- a/ironfish-cli/src/commands/chain/rewind.ts +++ b/ironfish-cli/src/commands/chain/rewind.ts @@ -8,8 +8,9 @@ import { LocalFlags } from '../../flags' import { ProgressBar, ProgressBarPresets } from '../../ui' export default class Rewind extends IronfishCommand { - static description = - 'Rewind the chain database to the given sequence by deleting all blocks with greater sequences' + static description = 'rewind the blockchain to a block' + + static hidden = true static args = { to: Args.string({ From 910ba36efa7a92916bd73c0ac95665d4f448ae43 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Thu, 18 Jul 2024 14:09:38 -0700 Subject: [PATCH 50/81] Delete readd-block command (#5148) This was added 3 years ago for development testing when the blockchain didn't even run yet. --- .../src/commands/chain/readd-block.ts | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 ironfish-cli/src/commands/chain/readd-block.ts diff --git a/ironfish-cli/src/commands/chain/readd-block.ts b/ironfish-cli/src/commands/chain/readd-block.ts deleted file mode 100644 index d8e0ce6293..0000000000 --- a/ironfish-cli/src/commands/chain/readd-block.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* 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 { Args, ux } from '@oclif/core' -import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' - -export default class ReAddBlock extends IronfishCommand { - static description = - 'Remove and readd a block on the chain if it has no other blocks after it' - - static hidden = true - - static flags = { - ...LocalFlags, - } - - static args = { - hash: Args.string({ - required: true, - description: 'The hash of the block in hex format', - }), - } - - async start(): Promise { - const { args } = await this.parse(ReAddBlock) - const hash = Buffer.from(args.hash, 'hex') - - ux.action.start(`Opening node`) - const node = await this.sdk.node() - await node.openDB() - await node.chain.open() - ux.action.stop('done.') - - const block = await node.chain.getBlock(hash) - - if (!block) { - this.log(`No block found with hash ${hash.toString('hex')}`) - return this.exit(0) - } - - await node.chain.removeBlock(hash) - await node.chain.addBlock(block) - - this.log('Block has been reimported.') - } -} From 29b2a80b89eda64c7be509f3ba09e9e66e7f58bb Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Thu, 18 Jul 2024 14:20:11 -0700 Subject: [PATCH 51/81] Move `chain:asset` to `chain:assets:info` (#5147) --- .../src/commands/chain/{asset.ts => assets/info.ts} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename ironfish-cli/src/commands/chain/{asset.ts => assets/info.ts} (84%) diff --git a/ironfish-cli/src/commands/chain/asset.ts b/ironfish-cli/src/commands/chain/assets/info.ts similarity index 84% rename from ironfish-cli/src/commands/chain/asset.ts rename to ironfish-cli/src/commands/chain/assets/info.ts index 565f34f328..7a810fb578 100644 --- a/ironfish-cli/src/commands/chain/asset.ts +++ b/ironfish-cli/src/commands/chain/assets/info.ts @@ -3,10 +3,10 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { BufferUtils } from '@ironfish/sdk' import { Args } from '@oclif/core' -import { IronfishCommand } from '../../command' -import { RemoteFlags } from '../../flags' +import { IronfishCommand } from '../../../command' +import { RemoteFlags } from '../../../flags' -export default class Asset extends IronfishCommand { +export default class AssetInfo extends IronfishCommand { static description = 'Get the asset info' static args = { @@ -21,7 +21,7 @@ export default class Asset extends IronfishCommand { } async start(): Promise { - const { args } = await this.parse(Asset) + const { args } = await this.parse(AssetInfo) const { id: assetId } = args const client = await this.sdk.connectRpc() From e7f7090d5f4085b916188456e083a1073897ce8c Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Thu, 18 Jul 2024 14:28:39 -0700 Subject: [PATCH 52/81] Move `blocks:show` to `chain:blocks:info` (#5141) --- .../src/commands/{blocks/show.ts => chain/blocks/info.ts} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename ironfish-cli/src/commands/{blocks/show.ts => chain/blocks/info.ts} (78%) diff --git a/ironfish-cli/src/commands/blocks/show.ts b/ironfish-cli/src/commands/chain/blocks/info.ts similarity index 78% rename from ironfish-cli/src/commands/blocks/show.ts rename to ironfish-cli/src/commands/chain/blocks/info.ts index 88cb579391..e1dd3a3f06 100644 --- a/ironfish-cli/src/commands/blocks/show.ts +++ b/ironfish-cli/src/commands/chain/blocks/info.ts @@ -2,10 +2,10 @@ * 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 { Args } from '@oclif/core' -import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' +import { IronfishCommand } from '../../../command' +import { LocalFlags } from '../../../flags' -export default class ShowBlock extends IronfishCommand { +export default class BlockInfo extends IronfishCommand { static description = 'Show the block header of a requested hash or sequence' static args = { @@ -20,7 +20,7 @@ export default class ShowBlock extends IronfishCommand { } async start(): Promise { - const { args } = await this.parse(ShowBlock) + const { args } = await this.parse(BlockInfo) const { search } = args const client = await this.sdk.connectRpc() From 0e0e18a977c4deba3bee6d4333310167c4960543 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Thu, 18 Jul 2024 14:47:11 -0700 Subject: [PATCH 53/81] Remove RPC chain/showChain (#5146) This renders the blockchain as an ASCII response. This should be done by fetching the chain and then rendering this yourself. This was just a debug tool used in the early days of Iron Fish. --- ironfish/src/rpc/clients/client.ts | 11 ----- ironfish/src/rpc/routes/chain/index.ts | 1 - ironfish/src/rpc/routes/chain/showChain.ts | 51 ---------------------- 3 files changed, 63 deletions(-) delete mode 100644 ironfish/src/rpc/routes/chain/showChain.ts diff --git a/ironfish/src/rpc/clients/client.ts b/ironfish/src/rpc/clients/client.ts index 0b47bdd5fc..fec43b2081 100644 --- a/ironfish/src/rpc/clients/client.ts +++ b/ironfish/src/rpc/clients/client.ts @@ -161,8 +161,6 @@ import type { SetConfigResponse, SetScanningRequest, SetScanningResponse, - ShowChainRequest, - ShowChainResponse, SignTransactionRequest, SignTransactionResponse, StopNodeResponse, @@ -917,15 +915,6 @@ export abstract class RpcClient { ).waitForEnd() }, - showChain: ( - params: ShowChainRequest = undefined, - ): Promise> => { - return this.request( - `${ApiNamespace.chain}/showChain`, - params, - ).waitForEnd() - }, - getTransactionStream: ( params: GetTransactionStreamRequest, ): RpcResponse => { diff --git a/ironfish/src/rpc/routes/chain/index.ts b/ironfish/src/rpc/routes/chain/index.ts index 003667fd9f..08b7744cf1 100644 --- a/ironfish/src/rpc/routes/chain/index.ts +++ b/ironfish/src/rpc/routes/chain/index.ts @@ -14,7 +14,6 @@ export * from './getDifficulty' export * from './getNetworkHashPower' export * from './getTransaction' export * from './getTransactionStream' -export * from './showChain' export * from './getConsensusParameters' export * from './getAsset' export * from './getNetworkInfo' diff --git a/ironfish/src/rpc/routes/chain/showChain.ts b/ironfish/src/rpc/routes/chain/showChain.ts deleted file mode 100644 index 25c4e5dfb5..0000000000 --- a/ironfish/src/rpc/routes/chain/showChain.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* 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 * as yup from 'yup' -import { Assert } from '../../../assert' -import { FullNode } from '../../../node' -import { ApiNamespace } from '../namespaces' -import { routes } from '../router' -import { renderChain } from './utils' - -export type ShowChainRequest = - | { - start?: number | null - stop?: number | null - } - | undefined - -export type ShowChainResponse = { - content: string[] -} - -export const ShowChainRequestSchema: yup.ObjectSchema = yup - .object({ - start: yup.number().nullable().optional(), - stop: yup.number().nullable().optional(), - }) - .optional() - -export const ShowChainResponseSchema: yup.ObjectSchema = yup - .object({ - content: yup.array(yup.string().defined()).defined(), - }) - .defined() - -/** - * Render the chain as ani ASCII graph of the block chain - */ -routes.register( - `${ApiNamespace.chain}/showChain`, - ShowChainRequestSchema, - async (request, context): Promise => { - Assert.isInstanceOf(context, FullNode) - - const content = await renderChain(context.chain, request.data?.start, request.data?.stop, { - indent: ' ', - work: false, - }) - - request.end({ content }) - }, -) From 38fcbffdf3f5579b4c32945c9686989f7b7acf9c Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Thu, 18 Jul 2024 16:36:01 -0700 Subject: [PATCH 54/81] Make rpc get network power request optional (#5151) --- ironfish/src/rpc/clients/client.ts | 2 +- .../src/rpc/routes/chain/getNetworkHashPower.test.ts | 3 ++- ironfish/src/rpc/routes/chain/getNetworkHashPower.ts | 12 +++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/ironfish/src/rpc/clients/client.ts b/ironfish/src/rpc/clients/client.ts index fec43b2081..77f9c52d82 100644 --- a/ironfish/src/rpc/clients/client.ts +++ b/ironfish/src/rpc/clients/client.ts @@ -907,7 +907,7 @@ export abstract class RpcClient { }, getNetworkHashPower: ( - params: GetNetworkHashPowerRequest, + params: GetNetworkHashPowerRequest = undefined, ): Promise> => { return this.request( `${ApiNamespace.chain}/getNetworkHashPower`, diff --git a/ironfish/src/rpc/routes/chain/getNetworkHashPower.test.ts b/ironfish/src/rpc/routes/chain/getNetworkHashPower.test.ts index 9192441121..698d66456d 100644 --- a/ironfish/src/rpc/routes/chain/getNetworkHashPower.test.ts +++ b/ironfish/src/rpc/routes/chain/getNetworkHashPower.test.ts @@ -26,7 +26,8 @@ describe('Route chain/getNetworkHashPower', () => { await Promise.all([expect(routeTest.node.chain).toAddBlock(block)]) await Promise.all([routeTest.node.wallet.scan()]) } - const response = await routeTest.client.chain.getNetworkHashPower({}) + const response = await routeTest.client.chain.getNetworkHashPower() + expect(response.content).toEqual( expect.objectContaining({ hashesPerSecond: expect.any(Number), diff --git a/ironfish/src/rpc/routes/chain/getNetworkHashPower.ts b/ironfish/src/rpc/routes/chain/getNetworkHashPower.ts index dd3f5c2416..6744bbc6ba 100644 --- a/ironfish/src/rpc/routes/chain/getNetworkHashPower.ts +++ b/ironfish/src/rpc/routes/chain/getNetworkHashPower.ts @@ -9,10 +9,12 @@ import { RpcValidationError } from '../../adapters' import { ApiNamespace } from '../namespaces' import { routes } from '../router' -export type GetNetworkHashPowerRequest = { - blocks?: number | null // number of blocks to look back - sequence?: number | null // the sequence of the latest block from when to estimate the network speed -} +export type GetNetworkHashPowerRequest = + | { + blocks?: number | null // number of blocks to look back + sequence?: number | null // the sequence of the latest block from when to estimate the network speed + } + | undefined export type GetNetworkHashPowerResponse = { hashesPerSecond: number @@ -26,7 +28,7 @@ export const GetNetworkHashPowerRequestSchema: yup.ObjectSchema = yup From c957642eeeca962c5c98bae53ffad92f0e5689d3 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Thu, 18 Jul 2024 16:49:29 -0700 Subject: [PATCH 55/81] Abstract out rendering network name (#5152) So we can reuse this in the CLI and make this consistent everywhere. --- ironfish/src/networks/network.ts | 12 ++---------- ironfish/src/networks/networkDefinition.ts | 11 +++++++++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/ironfish/src/networks/network.ts b/ironfish/src/networks/network.ts index f805dd5ce2..79fd7ce904 100644 --- a/ironfish/src/networks/network.ts +++ b/ironfish/src/networks/network.ts @@ -1,11 +1,10 @@ /* 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 } from '../assert' import { Consensus } from '../consensus' import { SerializedBlock } from '../primitives/block' import { MathUtils } from '../utils' -import { defaultNetworkName, isDefaultNetworkId, NetworkDefinition } from './networkDefinition' +import { isDefaultNetworkId, NetworkDefinition, renderNetworkName } from './networkDefinition' export class Network { readonly default: boolean @@ -23,14 +22,7 @@ export class Network { this.consensus = new Consensus({ ...definition.consensus }) this.genesis = definition.genesis this.bootstrapNodes = definition.bootstrapNodes - - if (this.default) { - const defaultName = defaultNetworkName(definition.id) - Assert.isNotUndefined(defaultName) - this.name = defaultName - } else { - this.name = `Custom Network ${definition.id}` - } + this.name = renderNetworkName(definition.id) } /** diff --git a/ironfish/src/networks/networkDefinition.ts b/ironfish/src/networks/networkDefinition.ts index 09a42fb333..30c6785e29 100644 --- a/ironfish/src/networks/networkDefinition.ts +++ b/ironfish/src/networks/networkDefinition.ts @@ -2,6 +2,7 @@ * 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 * as yup from 'yup' +import { Assert } from '../assert' import { ActivationSequence, Checkpoint, ConsensusParameters } from '../consensus' import { Config, InternalStore } from '../fileStores' import { FileSystem } from '../fileSystems' @@ -83,6 +84,16 @@ export function defaultNetworkName(networkId: number): string | undefined { } } +export function renderNetworkName(networkId: number): string { + if (isDefaultNetworkId(networkId)) { + const defaultName = defaultNetworkName(networkId) + Assert.isNotUndefined(defaultName) + return defaultName + } else { + return `Custom Network ${networkId}` + } +} + export async function getNetworkDefinition( config: Config, internal: InternalStore, From 0a3187f539a1d63e8004ca851d42879123a4b97e Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Thu, 18 Jul 2024 17:05:37 -0700 Subject: [PATCH 56/81] Add UI card element to CLI (#5153) So we can render these horizontal cards more easily --- ironfish-cli/src/ui/card.ts | 18 ++++++++++++++++++ ironfish-cli/src/ui/index.ts | 1 + 2 files changed, 19 insertions(+) create mode 100644 ironfish-cli/src/ui/card.ts diff --git a/ironfish-cli/src/ui/card.ts b/ironfish-cli/src/ui/card.ts new file mode 100644 index 0000000000..528f3c0bfc --- /dev/null +++ b/ironfish-cli/src/ui/card.ts @@ -0,0 +1,18 @@ +/* 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/. */ + +export function card(items: Record, extraPadding: number = 2): string { + const keys = Object.keys(items) + const longestKey = keys.reduce((p, c) => Math.max(p, c.length), 0) + + const result = [] + + for (const key of keys) { + const keyPadded = (key + ':').padEnd(longestKey + 1 + extraPadding) + const value = String(items[key]) + result.push(`${keyPadded} ${value}`) + } + + return result.join('\n') +} diff --git a/ironfish-cli/src/ui/index.ts b/ironfish-cli/src/ui/index.ts index 75326b65a6..3fe9d4bbff 100644 --- a/ironfish-cli/src/ui/index.ts +++ b/ironfish-cli/src/ui/index.ts @@ -5,3 +5,4 @@ export * from './prompt' export * from './progressBar' export * from './table' +export * from './card' From d021340d7f2350da726368f13fd61d846adc9d0a Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Fri, 19 Jul 2024 14:02:53 -0700 Subject: [PATCH 57/81] Add chain:status command (#5150) This new command shows more detailed information specifically about the blockchain than is shown by node status. --- ironfish-cli/src/commands/chain/status.ts | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 ironfish-cli/src/commands/chain/status.ts diff --git a/ironfish-cli/src/commands/chain/status.ts b/ironfish-cli/src/commands/chain/status.ts new file mode 100644 index 0000000000..a98e855378 --- /dev/null +++ b/ironfish-cli/src/commands/chain/status.ts @@ -0,0 +1,39 @@ +/* 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 { FileUtils, renderNetworkName } from '@ironfish/sdk' +import { IronfishCommand } from '../../command' +import { LocalFlags } from '../../flags' +import * as ui from '../../ui' + +export default class ChainStatus extends IronfishCommand { + static description = 'show the status of the chain' + + static flags = { + ...LocalFlags, + } + + async start(): Promise { + const client = await this.sdk.connectRpc() + + const [status, difficulty, power] = await Promise.all([ + client.node.getStatus(), + client.chain.getDifficulty(), + client.chain.getNetworkHashPower(), + ]) + + this.log( + ui.card({ + Network: renderNetworkName(status.content.node.networkId), + Blocks: status.content.blockchain.head.sequence, + Hash: status.content.blockchain.head.hash, + Time: new Date(status.content.blockchain.headTimestamp).toLocaleString(), + Synced: status.content.blockchain.synced, + Difficulty: difficulty.content.difficulty, + Size: FileUtils.formatFileSize(status.content.blockchain.dbSizeBytes), + Work: status.content.blockchain.dbSizeBytes, + Power: FileUtils.formatHashRate(power.content.hashesPerSecond), + }), + ) + } +} From cf0704bb4f776da5f9e77c94bbe2998f30b69694 Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Fri, 19 Jul 2024 14:17:35 -0700 Subject: [PATCH 58/81] Add `chain:transactions:info` command (#5149) * Add `chain:transactions:info` command * Use new ui card element --- .../src/commands/chain/transactions/info.ts | 50 +++++++++++++++++++ ironfish/src/rpc/clients/client.ts | 6 +-- .../src/rpc/routes/chain/getTransaction.ts | 2 +- 3 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 ironfish-cli/src/commands/chain/transactions/info.ts diff --git a/ironfish-cli/src/commands/chain/transactions/info.ts b/ironfish-cli/src/commands/chain/transactions/info.ts new file mode 100644 index 0000000000..e29968d31d --- /dev/null +++ b/ironfish-cli/src/commands/chain/transactions/info.ts @@ -0,0 +1,50 @@ +/* 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 { CurrencyUtils, FileUtils } from '@ironfish/sdk' +import { Args } from '@oclif/core' +import { IronfishCommand } from '../../../command' +import { RemoteFlags } from '../../../flags' +import * as ui from '../../../ui' + +export class TransactionInfo extends IronfishCommand { + static description = 'Display info about a transaction' + + static flags = { + ...RemoteFlags, + } + + static args = { + hash: Args.string({ + required: true, + description: 'Hash of the transaction', + }), + } + + async start(): Promise { + const { args } = await this.parse(TransactionInfo) + + const client = await this.sdk.connectRpc() + + const response = await client.chain.getTransaction({ + transactionHash: args.hash, + }) + + const transaction = response.content + + this.log( + ui.card({ + 'Block hash': transaction.blockHash, + 'Transaction hash': transaction.hash, + Fee: CurrencyUtils.render(transaction.fee.toString(), true), + 'Expiration sequence': transaction.expiration, + 'Transaction size': FileUtils.formatMemorySize(transaction.size), + 'Notes output': transaction.notes.length, + 'Notes spent': transaction.spends.length, + 'Mint count': transaction.mints.length, + 'Burn count': transaction.burns.length, + }), + ) + } +} diff --git a/ironfish/src/rpc/clients/client.ts b/ironfish/src/rpc/clients/client.ts index 77f9c52d82..cb179e9bfe 100644 --- a/ironfish/src/rpc/clients/client.ts +++ b/ironfish/src/rpc/clients/client.ts @@ -926,11 +926,11 @@ export abstract class RpcClient { getTransaction: ( params: GetTransactionRequest, - ): RpcResponse => { - return this.request( + ): Promise> => { + return this.request( `${ApiNamespace.chain}/getTransaction`, params, - ) + ).waitForEnd() }, getConsensusParameters: ( diff --git a/ironfish/src/rpc/routes/chain/getTransaction.ts b/ironfish/src/rpc/routes/chain/getTransaction.ts index 3719ba01a5..9f2c2d80a7 100644 --- a/ironfish/src/rpc/routes/chain/getTransaction.ts +++ b/ironfish/src/rpc/routes/chain/getTransaction.ts @@ -95,6 +95,7 @@ routes.register( const { transaction, initialNoteIndex } = foundTransaction const rawTransaction: GetTransactionResponse = { + blockHash: blockHashBuffer.toString('hex'), fee: Number(transaction.fee()), expiration: transaction.expiration(), hash: transaction.hash().toString('hex'), @@ -130,7 +131,6 @@ routes.register( commitment: spend.commitment.toString('hex'), size: spend.size, })), - blockHash: blockHashBuffer.toString('hex'), } request.end(rawTransaction) From cef70557bc7ca65c9d2988a67a031d79334f9b2e Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Mon, 22 Jul 2024 13:01:25 -0700 Subject: [PATCH 59/81] Adjust descriptions of chain commands (#5158) --- ironfish-cli/package.json | 6 ++++++ ironfish-cli/src/commands/chain/assets/info.ts | 2 +- ironfish-cli/src/commands/chain/broadcast.ts | 2 +- ironfish-cli/src/commands/chain/download.ts | 4 +--- ironfish-cli/src/commands/chain/export.ts | 2 +- ironfish-cli/src/commands/chain/forks.ts | 2 +- ironfish-cli/src/commands/chain/power.ts | 2 +- ironfish-cli/src/commands/chain/prune.ts | 4 +--- ironfish-cli/src/commands/chain/status.ts | 2 +- ironfish-cli/src/commands/chain/transactions/info.ts | 2 +- ironfish-cli/src/commands/wallet/prune.ts | 2 -- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ironfish-cli/package.json b/ironfish-cli/package.json index 06fe43f4d7..babd2a7cd9 100644 --- a/ironfish-cli/package.json +++ b/ironfish-cli/package.json @@ -104,6 +104,12 @@ "topics": { "wallet:scanning": { "description": "Turn on or off scanning for accounts" + }, + "chain:blocks": { + "description": "commands to look at blocks" + }, + "chain:assets": { + "description": "commands to look at assets" } } }, diff --git a/ironfish-cli/src/commands/chain/assets/info.ts b/ironfish-cli/src/commands/chain/assets/info.ts index 7a810fb578..e0a3ab2e60 100644 --- a/ironfish-cli/src/commands/chain/assets/info.ts +++ b/ironfish-cli/src/commands/chain/assets/info.ts @@ -7,7 +7,7 @@ import { IronfishCommand } from '../../../command' import { RemoteFlags } from '../../../flags' export default class AssetInfo extends IronfishCommand { - static description = 'Get the asset info' + static description = 'show asset information' static args = { id: Args.string({ diff --git a/ironfish-cli/src/commands/chain/broadcast.ts b/ironfish-cli/src/commands/chain/broadcast.ts index f862086893..8e2c5e644e 100644 --- a/ironfish-cli/src/commands/chain/broadcast.ts +++ b/ironfish-cli/src/commands/chain/broadcast.ts @@ -6,7 +6,7 @@ import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' export class BroadcastCommand extends IronfishCommand { - static description = `Broadcast a transaction to the network` + static description = 'broadcast a transaction to the network' static flags = { ...RemoteFlags, diff --git a/ironfish-cli/src/commands/chain/download.ts b/ironfish-cli/src/commands/chain/download.ts index fa1062e858..cad37a66f1 100644 --- a/ironfish-cli/src/commands/chain/download.ts +++ b/ironfish-cli/src/commands/chain/download.ts @@ -10,9 +10,7 @@ import { DownloadedSnapshot, getDefaultManifestUrl, SnapshotDownloader } from '. import { confirmOrQuit, ProgressBar, ProgressBarPresets } from '../../ui' export default class Download extends IronfishCommand { - static hidden = false - - static description = `Download and import a chain snapshot` + static description = 'download the chain' static flags = { ...LocalFlags, diff --git a/ironfish-cli/src/commands/chain/export.ts b/ironfish-cli/src/commands/chain/export.ts index 4d4618d7ec..0ba247ec35 100644 --- a/ironfish-cli/src/commands/chain/export.ts +++ b/ironfish-cli/src/commands/chain/export.ts @@ -9,7 +9,7 @@ import { RemoteFlags } from '../../flags' import { ProgressBar } from '../../ui' export default class Export extends IronfishCommand { - static description = 'Export part of the chain database to JSON' + static description = 'export the chain to a file' static flags = { ...RemoteFlags, diff --git a/ironfish-cli/src/commands/chain/forks.ts b/ironfish-cli/src/commands/chain/forks.ts index a682db74d1..1b1b0fa375 100644 --- a/ironfish-cli/src/commands/chain/forks.ts +++ b/ironfish-cli/src/commands/chain/forks.ts @@ -8,7 +8,7 @@ import { RemoteFlags } from '../../flags' import { GossipForkCounter } from '../../utils/gossipForkCounter' export default class ForksCommand extends IronfishCommand { - static description = 'Try to detect forks that are being mined' + static description = 'detect forks that are being mined' static flags = { ...RemoteFlags, diff --git a/ironfish-cli/src/commands/chain/power.ts b/ironfish-cli/src/commands/chain/power.ts index 1c67ad0a54..72bc2a8ac4 100644 --- a/ironfish-cli/src/commands/chain/power.ts +++ b/ironfish-cli/src/commands/chain/power.ts @@ -7,7 +7,7 @@ import { IronfishCommand } from '../../command' import { LocalFlags } from '../../flags' export default class Power extends IronfishCommand { - static description = "Show the network's hash power per second" + static description = "show the network's mining power" static flags = { ...LocalFlags, diff --git a/ironfish-cli/src/commands/chain/prune.ts b/ironfish-cli/src/commands/chain/prune.ts index d2bb61b5d4..ff1b493740 100644 --- a/ironfish-cli/src/commands/chain/prune.ts +++ b/ironfish-cli/src/commands/chain/prune.ts @@ -7,9 +7,7 @@ import { IronfishCommand } from '../../command' import { LocalFlags } from '../../flags' export default class Prune extends IronfishCommand { - static description = 'Remove old blocks from the chain' - - static hidden = false + static description = 'remove unused blocks from the chain' static flags = { ...LocalFlags, diff --git a/ironfish-cli/src/commands/chain/status.ts b/ironfish-cli/src/commands/chain/status.ts index a98e855378..0bfd9c1f45 100644 --- a/ironfish-cli/src/commands/chain/status.ts +++ b/ironfish-cli/src/commands/chain/status.ts @@ -7,7 +7,7 @@ import { LocalFlags } from '../../flags' import * as ui from '../../ui' export default class ChainStatus extends IronfishCommand { - static description = 'show the status of the chain' + static description = 'show chain information' static flags = { ...LocalFlags, diff --git a/ironfish-cli/src/commands/chain/transactions/info.ts b/ironfish-cli/src/commands/chain/transactions/info.ts index e29968d31d..d022987af4 100644 --- a/ironfish-cli/src/commands/chain/transactions/info.ts +++ b/ironfish-cli/src/commands/chain/transactions/info.ts @@ -9,7 +9,7 @@ import { RemoteFlags } from '../../../flags' import * as ui from '../../../ui' export class TransactionInfo extends IronfishCommand { - static description = 'Display info about a transaction' + static description = 'show transaction information' static flags = { ...RemoteFlags, diff --git a/ironfish-cli/src/commands/wallet/prune.ts b/ironfish-cli/src/commands/wallet/prune.ts index 9f81d6b18e..2deb840324 100644 --- a/ironfish-cli/src/commands/wallet/prune.ts +++ b/ironfish-cli/src/commands/wallet/prune.ts @@ -9,8 +9,6 @@ import { LocalFlags } from '../../flags' export default class PruneCommand extends IronfishCommand { static description = 'Removes expired transactions from the wallet' - static hidden = false - static flags = { ...LocalFlags, dryrun: Flags.boolean({ From 6051320b62f2fc6daaa213f3d3a2c47f61ae9da6 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Tue, 23 Jul 2024 12:48:54 -0700 Subject: [PATCH 60/81] Delete dupliacate RPC serializers (#5167) These were duplicated under the wrong file. Must have been due to a merge issue. --- ironfish/src/rpc/routes/chain/getBlock.ts | 3 +- ironfish/src/rpc/routes/chain/getBlocks.ts | 2 +- ironfish/src/rpc/routes/chain/utils.ts | 62 +--------------------- 3 files changed, 3 insertions(+), 64 deletions(-) diff --git a/ironfish/src/rpc/routes/chain/getBlock.ts b/ironfish/src/rpc/routes/chain/getBlock.ts index 375144903a..2eb759d842 100644 --- a/ironfish/src/rpc/routes/chain/getBlock.ts +++ b/ironfish/src/rpc/routes/chain/getBlock.ts @@ -10,9 +10,8 @@ import { GENESIS_BLOCK_SEQUENCE } from '../../../primitives/block' import { RpcNotFoundError, RpcValidationError } from '../../adapters' import { ApiNamespace } from '../namespaces' import { routes } from '../router' -import { serializeRpcBlockHeader } from './serializers' +import { serializeRpcBlockHeader, serializeRpcTransaction } from './serializers' import { RpcBlock, RpcBlockSchema } from './types' -import { serializeRpcTransaction } from './utils' export type GetBlockRequest = { search?: string diff --git a/ironfish/src/rpc/routes/chain/getBlocks.ts b/ironfish/src/rpc/routes/chain/getBlocks.ts index d6deca8ccb..40ead1e4da 100644 --- a/ironfish/src/rpc/routes/chain/getBlocks.ts +++ b/ironfish/src/rpc/routes/chain/getBlocks.ts @@ -8,8 +8,8 @@ import { BlockchainUtils } from '../../../utils/blockchain' import { RpcNotFoundError, RpcValidationError } from '../../adapters' import { ApiNamespace } from '../namespaces' import { routes } from '../router' +import { serializeRpcBlock } from './serializers' import { RpcBlock, RpcBlockSchema } from './types' -import { serializeRpcBlock } from './utils' export type GetBlocksRequest = { /** diff --git a/ironfish/src/rpc/routes/chain/utils.ts b/ironfish/src/rpc/routes/chain/utils.ts index 0d14f9abfe..77a5be4b7c 100644 --- a/ironfish/src/rpc/routes/chain/utils.ts +++ b/ironfish/src/rpc/routes/chain/utils.ts @@ -6,13 +6,8 @@ import { Assert } from '../../../assert' import { Blockchain } from '../../../blockchain' import { VerificationResult } from '../../../consensus/verifier' import { createRootLogger, Logger } from '../../../logger' -import { getBlockSize, getTransactionSize } from '../../../network/utils/serializers' -import { Block, Transaction } from '../../../primitives' import { BlockHeader } from '../../../primitives/blockheader' -import { BlockchainUtils, BufferUtils, HashUtils } from '../../../utils' -import { serializeRpcBlockHeader } from './serializers' -import { RpcBlock } from './types' -import { RpcTransaction } from './types' +import { BlockchainUtils, HashUtils } from '../../../utils' const DEFAULT_OPTIONS = { seq: true, @@ -166,58 +161,3 @@ export async function renderGraph( } } } - -export const serializeRpcBlock = (block: Block, serialized?: boolean): RpcBlock => { - const blockHeaderResponse = serializeRpcBlockHeader(block.header) - - const transactions: RpcTransaction[] = [] - for (const tx of block.transactions) { - transactions.push(serializeRpcTransaction(tx, serialized)) - } - - return { - ...blockHeaderResponse, - size: getBlockSize(block), - transactions, - } -} - -export const serializeRpcTransaction = ( - tx: Transaction, - serialized?: boolean, -): RpcTransaction => { - return { - hash: tx.hash().toString('hex'), - size: getTransactionSize(tx), - fee: Number(tx.fee()), - expiration: tx.expiration(), - notes: tx.notes.map((note) => ({ - commitment: note.hash().toString('hex'), - hash: note.hash().toString('hex'), - serialized: note.serialize().toString('hex'), - })), - spends: tx.spends.map((spend) => ({ - nullifier: spend.nullifier.toString('hex'), - commitment: spend.commitment.toString('hex'), - size: spend.size, - })), - mints: tx.mints.map((mint) => ({ - id: mint.asset.id().toString('hex'), - metadata: BufferUtils.toHuman(mint.asset.metadata()), - name: BufferUtils.toHuman(mint.asset.name()), - creator: mint.asset.creator().toString('hex'), - value: mint.value.toString(), - transferOwnershipTo: mint.transferOwnershipTo?.toString('hex'), - assetId: mint.asset.id().toString('hex'), - assetName: mint.asset.name().toString('hex'), - })), - burns: tx.burns.map((burn) => ({ - id: burn.assetId.toString('hex'), - value: burn.value.toString(), - assetId: burn.assetId.toString('hex'), - assetName: '', - })), - signature: tx.transactionSignature().toString('hex'), - ...(serialized ? { serialized: tx.serialize().toString('hex') } : {}), - } -} From c67e5618e650d7025c7fda31f45021f9603e13c9 Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:54:07 -0700 Subject: [PATCH 61/81] CLI command: replace unneeded type hacking with built-in ctor property (#5165) --- ironfish-cli/src/command.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ironfish-cli/src/command.ts b/ironfish-cli/src/command.ts index c1edd805cd..f9a4f9dfb1 100644 --- a/ironfish-cli/src/command.ts +++ b/ironfish-cli/src/command.ts @@ -109,10 +109,7 @@ export abstract class IronfishCommand extends Command { } async init(): Promise { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any - const commandClass = this.constructor as any - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - const { flags } = await this.parse(commandClass) + const { flags } = await this.parse(this.ctor) // Get the flags from the flag object which is unknown const dataDirFlag = getFlag(flags, DataDirFlagKey) From 904d035cbd06686c668b2003f69f4a7849d0b928 Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Tue, 23 Jul 2024 12:55:34 -0700 Subject: [PATCH 62/81] Add ability to use JSON flag with CLI commands (#5163) Also implements it for the `chain:blocks:info` command --- ironfish-cli/src/command.ts | 16 +++++++++---- .../src/commands/chain/blocks/info.ts | 23 +++++++++++++++++-- ironfish-cli/src/ui/index.ts | 5 ++-- ironfish-cli/src/ui/json.ts | 9 ++++++++ 4 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 ironfish-cli/src/ui/json.ts diff --git a/ironfish-cli/src/command.ts b/ironfish-cli/src/command.ts index f9a4f9dfb1..0534adc29e 100644 --- a/ironfish-cli/src/command.ts +++ b/ironfish-cli/src/command.ts @@ -11,7 +11,7 @@ import { Logger, RpcConnectionError, } from '@ironfish/sdk' -import { Command, Config } from '@oclif/core' +import { Command, Config, ux } from '@oclif/core' import { CLIError, ExitError } from '@oclif/core/errors' import { ConfigFlagKey, @@ -33,6 +33,7 @@ import { VerboseFlagKey, } from './flags' import { IronfishCliPKG } from './package' +import * as ui from './ui' import { hasUserResponseError } from './utils' export type SIGNALS = 'SIGTERM' | 'SIGINT' | 'SIGUSR2' @@ -73,11 +74,11 @@ export abstract class IronfishCommand extends Command { this.logger = createRootLogger().withTag(this.ctor.id) } - abstract start(): Promise | void + abstract start(): Promise | void - async run(): Promise { + async run(): Promise { try { - await this.start() + return await this.start() } catch (error: unknown) { if (hasUserResponseError(error)) { this.log(error.codeMessage) @@ -217,6 +218,13 @@ export abstract class IronfishCommand extends Command { closeFromSignal(signal: NodeJS.Signals): Promise { throw new Error(`Not implemented closeFromSignal: ${signal}`) } + + // Override the built-in logJson method to implement our own colorizer that + // works with default terminal colors instead of requiring a theme to be + // configured. + logJson(json: unknown): void { + ux.stdout(ui.json(json)) + } } function getFlag(flags: unknown, flag: FLAGS): unknown { diff --git a/ironfish-cli/src/commands/chain/blocks/info.ts b/ironfish-cli/src/commands/chain/blocks/info.ts index e1dd3a3f06..f836e924d9 100644 --- a/ironfish-cli/src/commands/chain/blocks/info.ts +++ b/ironfish-cli/src/commands/chain/blocks/info.ts @@ -1,9 +1,11 @@ /* 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 { BufferUtils, TimeUtils } from '@ironfish/sdk' import { Args } from '@oclif/core' import { IronfishCommand } from '../../../command' import { LocalFlags } from '../../../flags' +import * as ui from '../../../ui' export default class BlockInfo extends IronfishCommand { static description = 'Show the block header of a requested hash or sequence' @@ -19,13 +21,30 @@ export default class BlockInfo extends IronfishCommand { ...LocalFlags, } - async start(): Promise { + static enableJsonFlag: boolean = true + + async start(): Promise { const { args } = await this.parse(BlockInfo) const { search } = args const client = await this.sdk.connectRpc() const data = await client.chain.getBlock({ search }) + const blockData = data.content + + this.log( + ui.card({ + Hash: blockData.block.hash, + Confirmed: blockData.metadata.confirmed, + Fork: !blockData.metadata.main, + Sequence: blockData.block.sequence, + 'Previous Block Hash': blockData.block.previousBlockHash, + Difficulty: blockData.block.difficulty, + Timestamp: TimeUtils.renderString(blockData.block.timestamp), + Graffiti: BufferUtils.toHuman(Buffer.from(blockData.block.graffiti, 'hex')), + 'Transaction Count': blockData.block.transactions.length, + }), + ) - this.log(JSON.stringify(data.content, undefined, ' ')) + return blockData } } diff --git a/ironfish-cli/src/ui/index.ts b/ironfish-cli/src/ui/index.ts index 3fe9d4bbff..1b524bebe7 100644 --- a/ironfish-cli/src/ui/index.ts +++ b/ironfish-cli/src/ui/index.ts @@ -2,7 +2,8 @@ * 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/. */ -export * from './prompt' +export * from './card' +export * from './json' export * from './progressBar' +export * from './prompt' export * from './table' -export * from './card' diff --git a/ironfish-cli/src/ui/json.ts b/ironfish-cli/src/ui/json.ts new file mode 100644 index 0000000000..bcd1196370 --- /dev/null +++ b/ironfish-cli/src/ui/json.ts @@ -0,0 +1,9 @@ +/* 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 jsonColorizer from 'json-colorizer' + +export function json(data: unknown): string { + return jsonColorizer(JSON.stringify(data, undefined, ' ')) +} From 8b4481890171843d648100f4479b221eb79ec7f9 Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:32:31 -0700 Subject: [PATCH 63/81] Add missing offline flag (#5169) This flag doesn't quite work as intended, but having missing flags can sometimes throw compilation errors in certain scenarios. It should throw errors in all scenarios, but that will be tackled soon. --- ironfish-cli/src/commands/wallet/chainport/send.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ironfish-cli/src/commands/wallet/chainport/send.ts b/ironfish-cli/src/commands/wallet/chainport/send.ts index 324e9f5c82..17dfa6ddd8 100644 --- a/ironfish-cli/src/commands/wallet/chainport/send.ts +++ b/ironfish-cli/src/commands/wallet/chainport/send.ts @@ -77,6 +77,10 @@ export class BridgeCommand extends IronfishCommand { description: 'The block sequence after which the transaction will be removed from the mempool. Set to 0 for no expiration.', }), + offline: Flags.boolean({ + default: false, + description: 'Allow offline transaction creation', + }), } async start(): Promise { From 541660dc406d8914f23bc8a91c20fdf863789c23 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Tue, 23 Jul 2024 15:32:56 -0700 Subject: [PATCH 64/81] Dont serialize rpc notes if not transaction (#5166) This makes it so if you tell the RpcTransaction serializer not to include the serialized format, the notes will also not be included. --- .../__fixtures__/serializers.test.ts.fixture | 36 +++++++++++++++++++ .../src/rpc/routes/chain/serializers.test.ts | 26 ++++++++++++++ ironfish/src/rpc/routes/chain/serializers.ts | 22 ++++++++---- ironfish/src/rpc/routes/chain/types.ts | 4 +-- 4 files changed, 80 insertions(+), 8 deletions(-) create mode 100644 ironfish/src/rpc/routes/chain/__fixtures__/serializers.test.ts.fixture create mode 100644 ironfish/src/rpc/routes/chain/serializers.test.ts diff --git a/ironfish/src/rpc/routes/chain/__fixtures__/serializers.test.ts.fixture b/ironfish/src/rpc/routes/chain/__fixtures__/serializers.test.ts.fixture new file mode 100644 index 0000000000..25d15107b4 --- /dev/null +++ b/ironfish/src/rpc/routes/chain/__fixtures__/serializers.test.ts.fixture @@ -0,0 +1,36 @@ +{ + "Rpc Chain Serializers should optionally return serialized": [ + { + "value": { + "version": 4, + "id": "92879eb0-acc6-432f-be27-4086746a8f81", + "name": "test", + "spendingKey": "13ae2b816c006b1ca439bb3f1f34ad5c560418ab9b7127e6b35ac15586e6a7dc", + "viewKey": "3a2fded7a277601345c41ceee3582457beac30e2fc698c0a780e0151c4485decd57749d0e52cc41c0804ff5e97901b551e5f642216b2fe7e75af95e51c0a4ee0", + "incomingViewKey": "d18c6bbd9fcd1fa9902e3981a0cbbe80f0b12eb6bf53860041bb334e38766606", + "outgoingViewKey": "d585ac8112192346000cba3a17cdf1e931cefd669d18c969ebfdda5620d8047a", + "publicAddress": "9a56d5900d757ac4837f2fc800c7426a43d6e1d69c829c11f3c17af52e870757", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "scanningEnabled": true, + "proofAuthorizingKey": "438afe1b8a45c98ccc426857103a5c1c20a3cd0d13292fb40f790ed5f956a90b" + }, + "head": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + } + }, + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAszlMYP6jllK6GdKJuvklyEInkCA5L3y9zzcwbVpFhpiDRbRF0zL/rSRpqORJjsL3Aj02X8BFcx8a8aB7I1ri2/Nw5DofvDmQirYEQtli/cGLH71rd22qcT3WTsjL+G6Lsjgoif3L2nmuwkNo+dCEOBUmbUC50s8TXmBdxvc9D/wTzC5l8SFrle869DI0Lk9eTMzyGZnPxr9fDUG8Io0RaIdIFE+e9JCY85yiRR4PT5ukitq4bW2LPqCOyi0tdxGPtO0b2/iUxxYgXYJy5zklqIoHNUm09bTy91+n7Ey61WjzMy27WZ3PUqpG0oupHPvGYMBoR/OljUGwhg3p4o9yCve3+EkzQHNwNkExgq9AIQQ+XvWkp62iRR0nqtxwWtZeeJ752sZ2P7f5/LZic+LxmQMrARFWP59e9iyx4O0eNs49QHMUs9vxSG28U6CDd0kDvlWv6hfx++asQTuONFLNOGLKqOjmcKv/ZJAOxyTWMqz//OSVTj3rkz0uuwVYs4R2Qa69FRmaoadlAe5KTpTBiL83yY1rDaAKfjyC4hGLm5EzfE29qz1Z2aeHXZXsAMBVL+HoAEN9uLl25jqOCVq3JWWRCnhgfGV/G8YjEfu873eiZTy43HHdwklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwL4g+FyQGz8rRXCWSBRk+7I2l9plAKoyFyA573OzGWMVTno00WAQlRaBDu9wkH/fqF/WhxRSIUkFdcP78NOp+DA==" + } + ] +} \ No newline at end of file diff --git a/ironfish/src/rpc/routes/chain/serializers.test.ts b/ironfish/src/rpc/routes/chain/serializers.test.ts new file mode 100644 index 0000000000..27a445b9ba --- /dev/null +++ b/ironfish/src/rpc/routes/chain/serializers.test.ts @@ -0,0 +1,26 @@ +/* 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 { useMinersTxFixture } from '../../../testUtilities/fixtures' +import { createNodeTest } from '../../../testUtilities/nodeTest' +import { serializeRpcTransaction } from './serializers' + +describe('Rpc Chain Serializers', () => { + const nodeTest = createNodeTest() + + it('should optionally return serialized', async () => { + const transaction = await useMinersTxFixture(nodeTest.node) + + // Should include serialized formats + let serialized = serializeRpcTransaction(transaction, true) + expect(serialized.serialized).toBe(transaction.serialize().toString('hex')) + expect(serialized.notes[0].serialized).toBe( + transaction.notes[0].serialize().toString('hex'), + ) + + // Now should not include them + serialized = serializeRpcTransaction(transaction, false) + expect(serialized.serialized).not.toBeDefined() + expect(serialized.notes[0].serialized).not.toBeDefined() + }) +}) diff --git a/ironfish/src/rpc/routes/chain/serializers.ts b/ironfish/src/rpc/routes/chain/serializers.ts index 17f0d950a1..8da71e4d3b 100644 --- a/ironfish/src/rpc/routes/chain/serializers.ts +++ b/ironfish/src/rpc/routes/chain/serializers.ts @@ -4,8 +4,9 @@ import { getBlockSize, getTransactionSize } from '../../../network/utils/serializers' import { Block, BlockHeader, Target, Transaction } from '../../../primitives' +import { NoteEncrypted } from '../../../primitives/noteEncrypted' import { BufferUtils } from '../../../utils' -import { RpcBlock, RpcBlockHeader, RpcTransaction } from './types' +import { RpcBlock, RpcBlockHeader, RpcEncryptedNote, RpcTransaction } from './types' export function serializeRpcBlockHeader(header: BlockHeader): RpcBlockHeader { return { @@ -57,6 +58,17 @@ export const serializeRpcBlock = (block: Block, serialized?: boolean): RpcBlock } } +export const serializeRpcEncryptedNote = ( + note: NoteEncrypted, + serialized?: boolean, +): RpcEncryptedNote => { + return { + commitment: note.hash().toString('hex'), + hash: note.hash().toString('hex'), + ...(serialized ? { serialized: note.serialize().toString('hex') } : undefined), + } +} + export const serializeRpcTransaction = ( tx: Transaction, serialized?: boolean, @@ -66,11 +78,9 @@ export const serializeRpcTransaction = ( size: getTransactionSize(tx), fee: Number(tx.fee()), expiration: tx.expiration(), - notes: tx.notes.map((note) => ({ - commitment: note.hash().toString('hex'), - hash: note.hash().toString('hex'), - serialized: note.serialize().toString('hex'), - })), + notes: tx.notes.map((note) => { + return serializeRpcEncryptedNote(note, serialized) + }), spends: tx.spends.map((spend) => ({ nullifier: spend.nullifier.toString('hex'), commitment: spend.commitment.toString('hex'), diff --git a/ironfish/src/rpc/routes/chain/types.ts b/ironfish/src/rpc/routes/chain/types.ts index 4cc9847fea..9ca7ee0f4e 100644 --- a/ironfish/src/rpc/routes/chain/types.ts +++ b/ironfish/src/rpc/routes/chain/types.ts @@ -21,7 +21,7 @@ export const RpcSpendSchema: yup.ObjectSchema = yup export type RpcEncryptedNote = { hash: string - serialized: string + serialized?: string /** * @deprecated Please use hash instead */ @@ -32,7 +32,7 @@ export const RpcEncryptedNoteSchema: yup.ObjectSchema = yup .object({ commitment: yup.string().defined(), hash: yup.string().defined(), - serialized: yup.string().defined(), + serialized: yup.string().optional(), }) .defined() From 31b8a0292c242cce7523703422eecdf192a2d0f8 Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Tue, 23 Jul 2024 15:33:17 -0700 Subject: [PATCH 65/81] Convert existing commands to use new ui card (#5160) This converts a lot of our manually configured card layouts to use our new ui card element. This simplifies the formatting so we have more consistent, easier to use card formatting across the CLI. --- .../src/commands/chain/assets/info.ts | 19 +++++---- ironfish-cli/src/commands/chain/benchmark.ts | 18 +++++---- ironfish-cli/src/commands/mempool/status.ts | 14 ++++--- .../src/commands/miners/pools/status.ts | 34 +++++++++------- ironfish-cli/src/commands/rpc/status.ts | 23 ++++++----- ironfish-cli/src/commands/status.ts | 37 +++++++++--------- ironfish-cli/src/commands/wallet/balance.ts | 27 ++++++++----- ironfish-cli/src/commands/wallet/burn.ts | 16 +++++--- ironfish-cli/src/commands/wallet/mint.ts | 22 ++++++----- ironfish-cli/src/commands/wallet/send.ts | 20 ++++++---- .../src/commands/wallet/transaction/index.ts | 39 ++++++++++--------- 11 files changed, 155 insertions(+), 114 deletions(-) diff --git a/ironfish-cli/src/commands/chain/assets/info.ts b/ironfish-cli/src/commands/chain/assets/info.ts index e0a3ab2e60..f9b0c18be6 100644 --- a/ironfish-cli/src/commands/chain/assets/info.ts +++ b/ironfish-cli/src/commands/chain/assets/info.ts @@ -5,6 +5,7 @@ import { BufferUtils } from '@ironfish/sdk' import { Args } from '@oclif/core' import { IronfishCommand } from '../../../command' import { RemoteFlags } from '../../../flags' +import * as ui from '../../../ui' export default class AssetInfo extends IronfishCommand { static description = 'show asset information' @@ -27,12 +28,16 @@ export default class AssetInfo extends IronfishCommand { const client = await this.sdk.connectRpc() const data = await client.chain.getAsset({ id: assetId }) - this.log(`Name: ${BufferUtils.toHuman(Buffer.from(data.content.name, 'hex'))}`) - this.log(`Metadata: ${BufferUtils.toHuman(Buffer.from(data.content.metadata, 'hex'))}`) - this.log(`Creator: ${data.content.creator}`) - this.log(`Owner: ${data.content.owner}`) - this.log(`Supply: ${data.content.supply ?? 'N/A'}`) - this.log(`Identifier: ${data.content.id}`) - this.log(`Transaction Created: ${data.content.createdTransactionHash}`) + this.log( + ui.card({ + Name: BufferUtils.toHuman(Buffer.from(data.content.name, 'hex')), + Metadata: BufferUtils.toHuman(Buffer.from(data.content.metadata, 'hex')), + Creator: data.content.creator, + Owner: data.content.owner, + Supply: data.content.supply ?? 'N/A', + Identifier: data.content.id, + 'Transaction Created': data.content.createdTransactionHash, + }), + ) } } diff --git a/ironfish-cli/src/commands/chain/benchmark.ts b/ironfish-cli/src/commands/chain/benchmark.ts index 7c0a15237f..a00bcf9452 100644 --- a/ironfish-cli/src/commands/chain/benchmark.ts +++ b/ironfish-cli/src/commands/chain/benchmark.ts @@ -9,6 +9,7 @@ import path from 'path' import { IronfishCommand } from '../../command' import { LocalFlags } from '../../flags' import { IronfishCliPKG } from '../../package' +import * as ui from '../../ui' export default class Benchmark extends IronfishCommand { static description = @@ -78,7 +79,7 @@ export default class Benchmark extends IronfishCommand { return this.error(`Chain must have at least ${blocks} blocks`) } - if (!tempNode.chain.head.hash.equals(startingHeader?.hash)) { + if (!tempNode.chain.head.hash.equals(startingHeader.hash)) { return this.error(`The two chains do not match at sequence ${startingSequence}`) } @@ -158,11 +159,12 @@ function renderStatus( totalNotes: number, sequence?: number, ): string { - return `\ -Current Block ${sequence ? sequence.toString() : '-'} -Blocks Processed ${totalBlocks.toString()} -Blocks/sec ${totalMs ? (totalBlocks / (totalMs / 1000)).toFixed(2) : 0} -Transactions/sec ${totalMs ? (totalTransactions / (totalMs / 1000)).toFixed(2) : 0} -Spends/sec ${totalMs ? (totalSpends / (totalMs / 1000)).toFixed(2) : 0} -Notes/sec ${totalMs ? (totalNotes / (totalMs / 1000)).toFixed(2) : 0}` + return ui.card({ + 'Current Block': sequence ? sequence.toString() : '-', + 'Blocks Processed': totalBlocks.toString(), + 'Blocks/sec': totalMs ? (totalBlocks / (totalMs / 1000)).toFixed(2) : 0, + 'Transactions/sec': totalMs ? (totalTransactions / (totalMs / 1000)).toFixed(2) : 0, + 'Spends/sec': totalMs ? (totalSpends / (totalMs / 1000)).toFixed(2) : 0, + 'Notes/sec': totalMs ? (totalNotes / (totalMs / 1000)).toFixed(2) : 0, + }) } diff --git a/ironfish-cli/src/commands/mempool/status.ts b/ironfish-cli/src/commands/mempool/status.ts index 10aaf19087..740b61892d 100644 --- a/ironfish-cli/src/commands/mempool/status.ts +++ b/ironfish-cli/src/commands/mempool/status.ts @@ -7,6 +7,7 @@ import { Flags } from '@oclif/core' import blessed from 'blessed' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' +import * as ui from '../../ui' export default class Status extends IronfishCommand { static description = 'Show the status of the Mempool' @@ -63,10 +64,11 @@ function renderStatus(content: GetMempoolStatusResponse): string { const maxStorage = FileUtils.formatMemorySize(content.maxSizeBytes) const saturationPercentage = ((content.sizeBytes / content.maxSizeBytes) * 100).toFixed(2) - return `\ -Tx Count ${content.size} -Memory ${storage} / ${maxStorage} (${saturationPercentage}%) -Eviction Cache ${content.recentlyEvictedCache.size} / ${content.recentlyEvictedCache.maxSize} -Evictions ${content.evictions} -Head Sequence ${content.headSequence}` + return ui.card({ + 'Tx Count': content.size, + Memory: `${storage} / ${maxStorage} (${saturationPercentage}%)`, + 'Eviction Cache': `${content.recentlyEvictedCache.size} / ${content.recentlyEvictedCache.maxSize}`, + Evictions: content.evictions, + 'Head Sequence': content.headSequence, + }) } diff --git a/ironfish-cli/src/commands/miners/pools/status.ts b/ironfish-cli/src/commands/miners/pools/status.ts index ec24238bf5..0d9070a183 100644 --- a/ironfish-cli/src/commands/miners/pools/status.ts +++ b/ironfish-cli/src/commands/miners/pools/status.ts @@ -16,6 +16,7 @@ import { Flags } from '@oclif/core' import blessed from 'blessed' import dns from 'dns' import { IronfishCommand } from '../../../command' +import * as ui from '../../../ui' export class PoolStatus extends IronfishCommand { static description = `Show the status of a mining pool` @@ -105,23 +106,26 @@ export class PoolStatus extends IronfishCommand { } renderStatus(status: MiningStatusMessage): string { - let result = '' - result += `Status of mining pool '${status.name}':\n` - result += `Miners: ${status.miners}\n` - result += `Hashrate: ${FileUtils.formatHashRate(status.hashRate)}\n` - result += `Shares pending payout: ${status.sharesPending}\n` - result += `Clients: ${status.clients}\n` - result += `Bans: ${status.bans}\n` + let data: Record = { + 'Status of mining pool': status.name, + Miners: status.miners, + Hashrate: FileUtils.formatHashRate(status.hashRate), + 'Shares pending payout': status.sharesPending, + Clients: status.clients, + Bans: status.bans, + } if (status.addressStatus) { - result += `\nMining status for address '${status.addressStatus.publicAddress}':\n` - result += `Number of miners: ${status.addressStatus.miners}\n` - result += `Connected miners: ${status.addressStatus.connectedMiners.join(', ')}\n` - result += `Hashrate: ${FileUtils.formatHashRate( - status.addressStatus.hashRate, - )}\n` - result += `Shares pending payout: ${status.addressStatus.sharesPending}` + data = { + ...data, + 'Mining status for address': status.addressStatus.publicAddress, + 'Number of miners': status.addressStatus.miners, + 'Connected miners': status.addressStatus.connectedMiners.join(', '), + Hashrate: FileUtils.formatHashRate(status.addressStatus.hashRate), + 'Shares pending payout': status.addressStatus.sharesPending, + } } - return result + + return ui.card(data) } } diff --git a/ironfish-cli/src/commands/rpc/status.ts b/ironfish-cli/src/commands/rpc/status.ts index bd1bd874ca..0ef3fdac11 100644 --- a/ironfish-cli/src/commands/rpc/status.ts +++ b/ironfish-cli/src/commands/rpc/status.ts @@ -6,6 +6,7 @@ import { Flags } from '@oclif/core' import blessed from 'blessed' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' +import * as ui from '../../ui' export default class Status extends IronfishCommand { static description = 'Show the status of the RPC layer' @@ -62,16 +63,18 @@ function renderStatus(content: GetRpcStatusResponse): string { for (const adapter of content.adapters) { result += `\n\n[${adapter.name}]\n` - result += `Clients: ${adapter.clients}\n` - result += `Requests Pending: ${adapter.pending.length}\n` - result += `Routes Pending: ${adapter.pending.join(', ')}\n` - result += `Inbound Traffic: ${FileUtils.formatMemorySize(adapter.inbound)}/s\n` - result += `Outbound Traffic: ${FileUtils.formatMemorySize(adapter.outbound)}/s\n` - result += `Outbound Total: ${FileUtils.formatMemorySize(adapter.writtenBytes)}\n` - result += `Inbound Total: ${FileUtils.formatMemorySize(adapter.readBytes)}\n` - result += `RW Backlog: ${FileUtils.formatMemorySize( - adapter.readableBytes, - )} / ${FileUtils.formatMemorySize(adapter.writableBytes)}` + result += ui.card({ + Clients: adapter.clients, + 'Requests Pending': adapter.pending.length, + 'Routes Pending': adapter.pending.join(', '), + 'Inbound Traffic': FileUtils.formatMemorySize(adapter.inbound), + 'Outbound Traffic': FileUtils.formatMemorySize(adapter.outbound), + 'Outbound Total': FileUtils.formatMemorySize(adapter.writtenBytes), + 'Inbound Total': FileUtils.formatMemorySize(adapter.readBytes), + 'RW Backlog': `${FileUtils.formatMemorySize( + adapter.readableBytes, + )} / ${FileUtils.formatMemorySize(adapter.writableBytes)}`, + }) } return result diff --git a/ironfish-cli/src/commands/status.ts b/ironfish-cli/src/commands/status.ts index ab5cb01bfd..f341cfe3d6 100644 --- a/ironfish-cli/src/commands/status.ts +++ b/ironfish-cli/src/commands/status.ts @@ -13,6 +13,7 @@ import { Flags } from '@oclif/core' import blessed from 'blessed' import { IronfishCommand } from '../command' import { RemoteFlags } from '../flags' +import * as ui from '../ui' export default class Status extends IronfishCommand { static description = 'Show the status of the node' @@ -214,22 +215,22 @@ function renderStatus(content: GetNodeStatusResponse, debugOutput: boolean): str ? [cores, current, rollingAvg].join(', ') : [cores, current].join(', ') - return `\ -Version ${content.node.version} @ ${content.node.git} -Node ${nodeStatus} -Node Name ${content.node.nodeName} -Peer ID ${content.peerNetwork.publicIdentity} -Block Graffiti ${blockGraffiti} -Network ${network} -Memory ${memoryStatus} -CPU ${cpuStatus} -P2P Network ${peerNetworkStatus} -Mining ${miningDirectorStatus} -Mem Pool ${memPoolStatus} -Syncer ${blockSyncerStatus} -Blockchain ${blockchainStatus} -Accounts ${accountStatus} -Telemetry ${telemetryStatus} -Workers ${workersStatus} -` + return ui.card({ + Version: `${content.node.version} @ ${content.node.git}`, + Node: nodeStatus, + 'Node Name': content.node.nodeName, + 'Peed ID': content.peerNetwork.publicIdentity, + 'Block Graffiti': blockGraffiti, + Network: network, + Memory: memoryStatus, + CPU: cpuStatus, + 'P2P Network': peerNetworkStatus, + Mining: miningDirectorStatus, + 'Mem Pool': memPoolStatus, + Syncer: blockSyncerStatus, + Blockchain: blockchainStatus, + Accounts: accountStatus, + Telemetry: telemetryStatus, + Workers: workersStatus, + }) } diff --git a/ironfish-cli/src/commands/wallet/balance.ts b/ironfish-cli/src/commands/wallet/balance.ts index be4f21b4b2..bc2a69bbbe 100644 --- a/ironfish-cli/src/commands/wallet/balance.ts +++ b/ironfish-cli/src/commands/wallet/balance.ts @@ -5,6 +5,7 @@ import { CurrencyUtils, GetBalanceResponse, isNativeIdentifier, RpcAsset } from import { Args, Flags } from '@oclif/core' import { IronfishCommand } from '../../command' import { RemoteFlags } from '../../flags' +import * as ui from '../../ui' import { renderAssetWithVerificationStatus } from '../../utils' export class BalanceCommand extends IronfishCommand { @@ -86,18 +87,26 @@ export class BalanceCommand extends IronfishCommand { const renderedUnconfirmed = renderValue(response.content.unconfirmed, asset, assetName) const renderedPending = renderValue(response.content.pending, asset, assetName) if (flags.all) { - this.log(`Account: ${response.content.account}`) - this.log(`Head Hash: ${response.content.blockHash || 'NULL'}`) - this.log(`Head Sequence: ${response.content.sequence || 'NULL'}`) - this.log(`Available: ${renderedAvailable}`) - this.log(`Confirmed: ${renderedConfirmed}`) - this.log(`Unconfirmed: ${renderedUnconfirmed}`) - this.log(`Pending: ${renderedPending}`) + this.log( + ui.card({ + Account: response.content.account, + 'Head Hash': response.content.blockHash || 'NULL', + 'Head Sequence': response.content.sequence || 'NULL', + Available: renderedAvailable, + Confirmed: renderedConfirmed, + Unconfirmed: renderedUnconfirmed, + Pending: renderedPending, + }), + ) return } - this.log(`Account: ${response.content.account}`) - this.log(`Available Balance: ${renderedAvailable}`) + this.log( + ui.card({ + Account: response.content.account, + 'Available Balance': renderedAvailable, + }), + ) } explainBalance(response: GetBalanceResponse, asset: RpcAsset, assetName: string): void { diff --git a/ironfish-cli/src/commands/wallet/burn.ts b/ironfish-cli/src/commands/wallet/burn.ts index cc751e368c..7754011ee6 100644 --- a/ironfish-cli/src/commands/wallet/burn.ts +++ b/ironfish-cli/src/commands/wallet/burn.ts @@ -13,7 +13,7 @@ import { import { Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../command' import { IronFlag, RemoteFlags, ValueFlag } from '../../flags' -import { confirmOrQuit } from '../../ui' +import * as ui from '../../ui' import { selectAsset } from '../../utils/asset' import { promptCurrency } from '../../utils/currency' import { promptExpiration } from '../../utils/expiration' @@ -268,10 +268,14 @@ export class Burn extends IronfishCommand { ) this.log(`Burned asset ${assetName} from ${account}`) - this.log(`Asset Identifier: ${assetId}`) - this.log(`Amount: ${renderedAmount}`) - this.log(`Hash: ${transaction.hash().toString('hex')}`) - this.log(`Fee: ${CurrencyUtils.render(transaction.fee(), true)}`) + this.log( + ui.card({ + 'Asset Identifier': assetId, + Amount: renderedAmount, + Hash: transaction.hash().toString('hex'), + Fee: CurrencyUtils.render(transaction.fee(), true), + }), + ) const networkId = (await client.chain.getNetworkInfo()).content.networkId const transactionUrl = getExplorer(networkId)?.getTransactionUrl( @@ -304,7 +308,7 @@ export class Burn extends IronfishCommand { const renderedAmount = CurrencyUtils.render(amount, true, asset.id, asset.verification) const renderedFee = CurrencyUtils.render(fee, true) - await confirmOrQuit( + await ui.confirmOrQuit( `You are about to burn ${renderedAmount} plus a transaction fee of ${renderedFee} with the account ${account}\nDo you confirm?`, confirm, ) diff --git a/ironfish-cli/src/commands/wallet/mint.ts b/ironfish-cli/src/commands/wallet/mint.ts index 93df359872..86e7519c25 100644 --- a/ironfish-cli/src/commands/wallet/mint.ts +++ b/ironfish-cli/src/commands/wallet/mint.ts @@ -17,7 +17,7 @@ import { import { Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../command' import { IronFlag, RemoteFlags, ValueFlag } from '../../flags' -import { confirmOrQuit, confirmPrompt, inputPrompt } from '../../ui' +import * as ui from '../../ui' import { selectAsset } from '../../utils/asset' import { promptCurrency } from '../../utils/currency' import { promptExpiration } from '../../utils/expiration' @@ -153,16 +153,16 @@ export class Mint extends IronfishCommand { // name is provided let isMintingNewAsset = Boolean(name || metadata) if (!assetId && !metadata && !name) { - isMintingNewAsset = await confirmPrompt('Do you want to create a new asset?') + isMintingNewAsset = await ui.confirmPrompt('Do you want to create a new asset?') } if (isMintingNewAsset) { if (!name) { - name = await inputPrompt('Enter the name for the new asset', true) + name = await ui.inputPrompt('Enter the name for the new asset', true) } if (!metadata) { - metadata = await inputPrompt('Enter metadata for the new asset') + metadata = await ui.inputPrompt('Enter metadata for the new asset') } const newAsset = new Asset(accountPublicKey, name, metadata) @@ -339,10 +339,14 @@ export class Mint extends IronfishCommand { ) const renderedFee = CurrencyUtils.render(transaction.fee(), true) this.log(`Minted asset ${BufferUtils.toHuman(minted.asset.name())} from ${account}`) - this.log(`Asset Identifier: ${minted.asset.id().toString('hex')}`) - this.log(`Value: ${renderedValue}`) - this.log(`Fee: ${renderedFee}`) - this.log(`Hash: ${transaction.hash().toString('hex')}`) + this.log( + ui.card({ + 'Asset Identifier': minted.asset.id().toString('hex'), + Value: renderedValue, + Fee: renderedFee, + Hash: transaction.hash().toString('hex'), + }), + ) const networkId = (await client.chain.getNetworkInfo()).content.networkId const transactionUrl = getExplorer(networkId)?.getTransactionUrl( @@ -401,6 +405,6 @@ export class Mint extends IronfishCommand { confirmMessage.push('Do you confirm?') - await confirmOrQuit(confirmMessage.join('\n'), confirm) + await ui.confirmOrQuit(confirmMessage.join('\n'), confirm) } } diff --git a/ironfish-cli/src/commands/wallet/send.ts b/ironfish-cli/src/commands/wallet/send.ts index 5e1996f5dd..70690812e9 100644 --- a/ironfish-cli/src/commands/wallet/send.ts +++ b/ironfish-cli/src/commands/wallet/send.ts @@ -15,7 +15,7 @@ import { import { Flags } from '@oclif/core' import { IronfishCommand } from '../../command' import { HexFlag, IronFlag, RemoteFlags, ValueFlag } from '../../flags' -import { confirmOrQuit, inputPrompt } from '../../ui' +import * as ui from '../../ui' import { selectAsset } from '../../utils/asset' import { promptCurrency } from '../../utils/currency' import { promptExpiration } from '../../utils/expiration' @@ -205,10 +205,10 @@ export class Send extends IronfishCommand { } if (!to) { - to = await inputPrompt('Enter the public address of the recipient', true) + to = await ui.inputPrompt('Enter the public address of the recipient', true) } - const memo = flags.memo ?? (await inputPrompt('Enter the memo (or leave blank)')) + const memo = flags.memo ?? (await ui.inputPrompt('Enter the memo (or leave blank)')) if (!isValidPublicAddress(to)) { this.log(`A valid public address is required`) @@ -293,7 +293,7 @@ export class Send extends IronfishCommand { ) } - await confirmOrQuit('', flags.confirm) + await ui.confirmOrQuit('', flags.confirm) transactionTimer.start() @@ -341,9 +341,13 @@ export class Send extends IronfishCommand { ) const renderedFee = CurrencyUtils.render(transaction.fee(), true) this.log(`Sent ${renderedAmount} to ${to} from ${from}`) - this.log(`Hash: ${transaction.hash().toString('hex')}`) - this.log(`Fee: ${renderedFee}`) - this.log(`Memo: ${memo}`) + this.log( + ui.card({ + Hash: transaction.hash().toString('hex'), + Fee: renderedFee, + Memo: memo, + }), + ) const networkId = (await client.chain.getNetworkInfo()).content.networkId const transactionUrl = getExplorer(networkId)?.getTransactionUrl( @@ -420,7 +424,7 @@ export class Send extends IronfishCommand { this.log(`\nHash: ${transaction.hash().toString('hex')}`) this.log(`Fee: ${CurrencyUtils.render(transaction.fee(), true)}`) - await confirmOrQuit('', confirm) + await ui.confirmOrQuit('', confirm) const addTransactionResponse = await client.wallet.addTransaction({ transaction: signedTransaction, diff --git a/ironfish-cli/src/commands/wallet/transaction/index.ts b/ironfish-cli/src/commands/wallet/transaction/index.ts index b6b0b821e8..f20d0dd899 100644 --- a/ironfish-cli/src/commands/wallet/transaction/index.ts +++ b/ironfish-cli/src/commands/wallet/transaction/index.ts @@ -12,7 +12,7 @@ import { import { Args, Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../../command' import { RemoteFlags } from '../../../flags' -import { table } from '../../../ui' +import * as ui from '../../../ui' import { displayChainportTransactionSummary, extractChainportDataFromTransaction, @@ -71,24 +71,27 @@ export class TransactionCommand extends IronfishCommand { const renderedFee = CurrencyUtils.render(transaction.fee, true) const explorerUrl = getExplorer(networkId)?.getTransactionUrl(hash) - this.log(`Transaction: ${hash}`) + const data: Record = { + Transaction: hash, + } if (explorerUrl) { - this.log(`Explorer: ${explorerUrl}`) + data['Explorer'] = explorerUrl } - this.log(`Account: ${response.content.account}`) - this.log(`Status: ${transaction.status}`) - this.log(`Type: ${transaction.type}`) - this.log(`Timestamp: ${TimeUtils.renderString(transaction.timestamp)}`) - this.log(`Fee: ${renderedFee}`) + data['Account'] = response.content.account + data['Status'] = transaction.status + data['Type'] = transaction.type + data['Timestamp'] = TimeUtils.renderString(transaction.timestamp) + data['Fee'] = renderedFee if (transaction.blockHash && transaction.blockSequence) { - this.log(`Block Hash: ${transaction.blockHash}`) - this.log(`Block Sequence: ${transaction.blockSequence}`) + data['Block Hash'] = transaction.blockHash + data['Block Sequence'] = transaction.blockSequence } - this.log(`Notes Count: ${transaction.notes.length}`) - this.log(`Spends Count: ${transaction.spends.length}`) - this.log(`Mints Count: ${transaction.mints.length}`) - this.log(`Burns Count: ${transaction.burns.length}`) - this.log(`Sender: ${transaction.notes[0].sender}`) + data['Notes Count'] = transaction.notes.length + data['Spends Count'] = transaction.spends.length + data['Mints Count'] = transaction.mints.length + data['Burns Count'] = transaction.burns.length + data['Sender'] = transaction.notes[0].sender + this.log(ui.card(data)) const chainportTxnDetails = extractChainportDataFromTransaction(networkId, transaction) @@ -127,7 +130,7 @@ export class TransactionCommand extends IronfishCommand { }) } - table(noteAssetPairs, { + ui.table(noteAssetPairs, { amount: { header: 'Amount', get: ({ asset, note }) => @@ -158,7 +161,7 @@ export class TransactionCommand extends IronfishCommand { if (transaction.spends.length > 0) { this.log(`\n---Spends---\n`) - table(transaction.spends, { + ui.table(transaction.spends, { size: { header: 'Size', get: (spend) => spend.size, @@ -184,7 +187,7 @@ export class TransactionCommand extends IronfishCommand { ) this.log(`\n---Asset Balance Deltas---\n`) - table(assetBalanceDeltas, { + ui.table(assetBalanceDeltas, { assetId: { header: 'Asset ID', get: (assetBalanceDelta) => assetBalanceDelta.assetId, From db33b409d3c59ce25d00c610f94ba96795233f56 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Tue, 23 Jul 2024 15:44:16 -0700 Subject: [PATCH 66/81] Update style guide to document command output info (#5162) * Update style guide to document command output info This should help users figure out how to design the output of their commands. * Update ironfish-cli/STYLE_GUIDE.md Co-authored-by: mat-if <97762857+mat-if@users.noreply.github.com> --------- Co-authored-by: mat-if <97762857+mat-if@users.noreply.github.com> --- ironfish-cli/STYLE_GUIDE.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ironfish-cli/STYLE_GUIDE.md b/ironfish-cli/STYLE_GUIDE.md index 39fa4d2801..80bbd93412 100644 --- a/ironfish-cli/STYLE_GUIDE.md +++ b/ironfish-cli/STYLE_GUIDE.md @@ -69,6 +69,20 @@ However, if prompting is required to complete a command, this means the user wil ## Output +When designing the output for a command, commands should output human readable output and not machine readable output. This means you should use components under the `ui` module such as `card`, `table`, or normal logs. It's fine if you only display a simplified version of the output. If the user needs the full data in machine readable format they can use the `--json` flag. + +You can categorize commands in a few ways, and you will design their output differently depending on the purpose of the command. You have output commands (status, chain:blocks:info, wallet:transactions), operation commands (stop, wallet:rename). + +### JSON Output + +We want to support JSON output in all data commands. This will allow developers to use our CLI for basic automating purposes avoiding the need to set up an HTTP client. + +If a command returns data it should have `static enableJsonFlag = true` and return an object with the JSON data in the command. The output JSON will automatically be colorized. See more here, https://oclif.io/docs/json/ + +It's OK to both return an object and use `log` even if JSON is not enabled. If you need custom logic and don't want to rely on returning the JSON, you can use `jsonEnabled()` and the `ui.json()` component to manually log colorized JSON. + +This is not necessary for operation commands that perform actions and quit such as `wallet:rename`. + ### Progress Many commands need to run long running operations. The CLI should not look like it's unresponsive. For example, `ironfish wallet:post` posts a transaction and optionally sends it to the network: From 04dcfaa05145e674e94e5c1365c000ea6c9c5f4b Mon Sep 17 00:00:00 2001 From: Hugh Cunningham <57735705+hughy@users.noreply.github.com> Date: Tue, 23 Jul 2024 16:18:57 -0700 Subject: [PATCH 67/81] refreshes WalletScanner scanningAccounts on disconnect (#5161) the WalletScanner uses a hash, 'scanFrom', for each account in its scanningAccounts list to check whether it should decrypt notes for the account. if scanFrom is null or equal to the previous block hash then notes are decrypted when connecting a block. scanFrom is set to the account head when calling refreshScanningAccounts and set to null when connecting a block if scanFrom is equal to the previous hash. however, when blocks are disconnected scanFrom is not currently changed. in the event of a reorg, decryption is skipped when connecting blocks from the fork if 'scanFrom' is still set to the hash of a disconnected block. regenerates fixtures for test of this behavior and updates disconnectBlock to set scanFrom to null for all accounts where it is equal to the disconnected block hash. --- .../__fixtures__/wallet.test.slow.ts.fixture | 224 +++++++++--------- ironfish/src/wallet/scanner/walletScanner.ts | 6 + 2 files changed, 118 insertions(+), 112 deletions(-) diff --git a/ironfish/src/wallet/__fixtures__/wallet.test.slow.ts.fixture b/ironfish/src/wallet/__fixtures__/wallet.test.slow.ts.fixture index f1c2f574d4..d95983e5be 100644 --- a/ironfish/src/wallet/__fixtures__/wallet.test.slow.ts.fixture +++ b/ironfish/src/wallet/__fixtures__/wallet.test.slow.ts.fixture @@ -351,118 +351,6 @@ ] } ], - "Wallet Removes notes when rolling back a fork": [ - { - "value": { - "version": 4, - "id": "7c33400f-4467-4ca2-9fe8-753ec9b990e9", - "name": "testA", - "spendingKey": "8819793463a99aa9bb8054a9947d8963887af88c117d76f3afe8794857338031", - "viewKey": "9369194732ba2e85c96597feb003bcd1f5bbea96cf281f2b4ce602b592e75184aecd8034da8e4b86c006b1fb9fe3b0fca55757c3ad5f2a6221de50c5039b2441", - "incomingViewKey": "9cbf9085d17168e11796f7c7f6f8521293b5f21ca491a9eeb8212c6c0dc9d004", - "outgoingViewKey": "aa3070df98bff103850a048b079a4fb0fa084423585fd94655472710e50b3a4c", - "publicAddress": "4d1ffb1e38788f1d5ef3978d9680d08a4f402b40b43a7faabde3e3fbe544dc40", - "createdAt": null, - "scanningEnabled": true, - "proofAuthorizingKey": "b785af62a34f29e9080590f00a517fe3a73a66da2a39f5907731946f44ea2103" - }, - "head": null - }, - { - "value": { - "version": 4, - "id": "4729cfd4-1db0-4aca-94f8-45801c8edd58", - "name": "testB", - "spendingKey": "7dfa306dc31b259cfb933b09a4fe637a2afa459e35d5727c99bfe2ed42cfbbe7", - "viewKey": "ef97818417b95aca5234ba933b69ba76754ca35b897d15d977d20597aa0b70d808a6002dbef6140c092c212f1750df973cd2815041369abb8798f442822cce67", - "incomingViewKey": "dba7925a07a584b375b2d85182e68e595034ecb8839aacd6fbfb05b7bf9f1606", - "outgoingViewKey": "a5999a8e17629cbe4758bcd70edb21adf9e7ec675c092a0f4844b82ddc8aaf8f", - "publicAddress": "b244bd095c6c835d11f1f833af36984bf1589c2d842141077b609ce559d24f0b", - "createdAt": null, - "scanningEnabled": true, - "proofAuthorizingKey": "6325e463f2330133da4dd139d7c6e3c9b54001fce7f8cb42d3b9ac63af8bbd00" - }, - "head": null - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:eTeIzsrYB3NQ6RX5Sf5ubtb2ZK0xwSRoZscdD2xknGI=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:BYxTG09lEE7p6FfekOb2/seCpBW0+1qCTEW456REE0E=" - }, - "target": "115792089237316195423570985008687907853269984665640564039457584007913129639935", - "randomness": "0", - "timestamp": 1717545209094, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAXP/k0xped3BqaHPSSnDHj0dKjf/i0RAy5syReq74xma3c5VwfJrKlc9as6Np9Xw7b7uuEZnP3xcWXPGRUvup20+H+NnvsLMqJDo0iYTdH8y1URnQkJ+oSLvsJoK0LdFtyY16+HIC6Y18iM/kTecU6k3vZf8i6dcV4vQ2bhskft8Ipwv072ieMzxZgqXSfsPYRY42CqSDEwuDQBRtiZEm9drFyn1W4QaxdiorVLmv0gyMw1zA04U0DTJLNQEdzEDb1heJ0JQMBu1Mlg5LmW0CdqNB8NO/oXgvJUVN5i88LHXWxw9wmUb0lm5Usvv9RSk7m5rfqOOJcLaf+xT69h22UD1MpxrDU2L+zO8cmV5+PYl7q0fORpNX884sgzrPPnQwbqEIfAS6dSlocejA9WsGWc/4QyV9zH3QYeF8lh9SXgI2EA+D0pMH0TDuksLnV1e+cdnAXc4AajmYdSNQeUjYTWVruLbHSXdPp0TMGqev6lnPr2k9w65pXBH42ha9HTOtHwPUqjZWm7qKs7MrQy4y0ccSneBnUldJwByapdAUIdzFV50jkg05FXKxEOubQbQl36X1sTCDeuK1FOeYsqHhD7bsct+6Z3Woct50a5N5jUO2OfAqu8uJwUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwgqpdDC1NdQYtnnnL1ZAe1EEKRp9SgXpaa/p3zzRG6DoWeR6A9wDIPF2Vkfppuh74kb6m45DaxisEzHl4WyDBAg==" - } - ] - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", - "noteCommitment": { - "type": "Buffer", - "data": "base64:/WHpUNJk1ZUbIs9RmVwBANo73YJmHzse6c2WIjwCggM=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:jGpU0dGtO0Bv/Rs9czI9bhcsdkJ76iq/Geu1OBr97k4=" - }, - "target": "115792089237316195423570985008687907853269984665640564039457584007913129639935", - "randomness": "0", - "timestamp": 1717545210192, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAARGfwAf6MqHGgnUk0FzqI9e8HmtQMst5vC6Cq2itpQZ+l4DlJjn/FnTFHpvGkX4PpdLRamZExTYynMZhd5YIr3MZaKXRaergW1ZHwTfK6FoWxPDhbQa8Y2K5ZlR0t3X3lx/XYWBvKNKxKass6OmNV6Q5CHWhCmFiLXQUtHlOUIyoPzm5qTYVl6rYbfoJxR/fisyLLiek7Kisf8+nU0EHA5xZ3xLP8RKt3U/AbjgT3MrOnSShG4SHj66KUcGbhbQWWpT1KAeI6qChxJJEY/JiAxQu2HaNb8lT6UozXGZQ2TLU5reigPE3skC0lfqphfkpt39P+Is7kQwuKEcR2aOw4IVH+oiy4g/Ti13/sbG3q1673fplAOFjanoUKtwWpPdVHZM9DCJkHx8myWn3M1bIawpeshjJaEcafq8NwMkbEBDBYcnucWnoy9I675oSM9TCJNfbFZwNbmAXdBIMRI+D215CUpPcVWYne1DzX9QzvpdgFhkA+4q18jk1FnkM26zG+q85Y0sktDWqpc1wQ3u4qzwMDLM4XD8C9Hj9t4B37Z/fSHN25WgE+BiP6wuw1A9OEEqatunY+kdfSPxlHxSqFdNVFBzCdSD1mzU0tprl0B9TTskHH1Oqni0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwOOQ+TS2zMKSq3CQeJ9eCEXhAbCJrgKWnyW76HEWZHLkGtJlGlhFcZBEFr6vag4vTnx0aADytG9cnCsrzMQyHAw==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "9F3001E47620F2F57BC58E64B3B7F543EB9BBEE131796BD8A9961AEF2B5CEA7F", - "noteCommitment": { - "type": "Buffer", - "data": "base64:JN9o1LxRZiNJVsVVw3g53baaD2a3XsH9OR5v1WrWqgg=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:HABCNl1jrXBk8rNSQy+5IJ7ZEBEy44XMAQshq9pqX2E=" - }, - "target": "115792089237316195423570985008687907853269984665640564039457584007913129639935", - "randomness": "0", - "timestamp": 1717545211266, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 5, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAC/xvrQ8NmY9V6PGSvqsqVsnw3u+FzuLk4Pls+LAWdlWHr/T4eJ+7Xm4dfqEjQR88kTGJlZZDhlCGB6J8bgo9HmIvW5imOBncG+nJjDMxEwyjeDQVe2xT4X0hxNbJ5eFXNKcGa80TZbC5e7hS/MrL0FYZoFZLWlHN9LrjairOBqAWIySKYRYwt9RwtMHEFc/TH9WiOTkdoV6ap7UwjMFEleYoAJr1Jxy2pwxxe8mrt9awf+sKF62vFrRdH6IlLX1mWB3kMjTbC1SOLAvfJxDWxoobVILziYJbeR/og/PyMSJx4nJ1Jo7tjXBY0KgsWKDyXkDHj0BFDoCcwzLZCkCDjOEtbVGJTcPddC1ADiDUk5Jd9UxbqTbRsRLGDrDZxqU5Q3VWFkBl8SScLO9CeSNT4EEzsaBjeejOGw1GTQ6nEIxcmR09O6RCDx3suZyCCXOINYrTv4cNuIzsdYnUbI1y+sWy5UuLc00HP66rYp3rVGeLQhBblxdOF5nURJHTfQfiFw7ubRsZg1sziSsrAEFFJvengDqP2+Wfvs1ckYJ/efHO6moKUIYaR2mGEuOUFicH/+XLiNDaNPUV/Wq99wfRDBYVT0xS7HKp0q+29Dbsb+QZ4qdLYwx7z0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwRWufRkwB7yAOVut2pYODDLbUVTLWIDiPnRanuCx2IzE10cHMe+bJvGKaUZx9reho0OBn3O2irqD/LHXyw2D7BA==" - } - ] - } - ], "Wallet View only accounts can observe received and spent notes": [ { "value": { @@ -1352,5 +1240,117 @@ } ] } + ], + "Wallet Removes notes when rolling back a fork": [ + { + "value": { + "version": 4, + "id": "cd32d3ae-a3f0-41e5-952c-ba430eac0a9d", + "name": "testA", + "spendingKey": "d1404bbc1140886f7f7be312029e254ea93369d59ffdefa45b9c5e96833bbb8a", + "viewKey": "9c9b854e1290ad846e75c8f747abedfcce7d68e41547157fc257afdccc429fec46b5aab2c46089bdd09c7fff38d8ae0f0faa699f97732c72b626853a5923e422", + "incomingViewKey": "a00560ae0c138fd4c2ae75ac8cb7b1869b4e90a8a7fa58728970547b6f721505", + "outgoingViewKey": "08e5fb5edc5eb8416cf151dc02812bb577d2ff2c96a7bbdac4ad10006b720553", + "publicAddress": "64cf78e2c1d923ca24a38863cd0a42f5f4bb42f3849375ba493a68d92a38401d", + "createdAt": null, + "scanningEnabled": true, + "proofAuthorizingKey": "dd370d899192be20a26816e195f6fce8651a3d501532198837d9d754e3d11c00" + }, + "head": null + }, + { + "value": { + "version": 4, + "id": "3c2f7db4-883c-4081-8ebe-dc1ddc9d6963", + "name": "testB", + "spendingKey": "9f8d5c3b61940b21e32279b68615b04e98f57953836761bb80e96ec19a902686", + "viewKey": "3bb41a4af34b71f766b6c17c154ef23b1b53d5bf22b13d34670f73c32722ef7099e33d5c888d843e865f66d6ea2518b2470110382c60b502f1edb70365385c89", + "incomingViewKey": "ba7298cad95c6b055576d38d901d3c29b5e4ba907351a29764c146e936d17800", + "outgoingViewKey": "1da9a40b0a70b2de121ae5c216d9a53efc3c565f7140154d98f5806cc3f8b7bc", + "publicAddress": "f00a99aa4bfe52a5749e36972bed4448575964eea74682f2759c4c9c225dda0f", + "createdAt": null, + "scanningEnabled": true, + "proofAuthorizingKey": "96b16ddb9f813186aff04cf38cf71fb13d25bbdf8ed9b79caa891552048e4709" + }, + "head": null + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:qt+/GjSXOiZu6QmwE195NyB23lGgdtqg9kZWVOTtg2g=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:vX9z4YThMKURqgF8vNiEuESHN1wVSc0SepIvDXo/TaE=" + }, + "target": "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "randomness": "0", + "timestamp": 1721686869438, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAArOIj6Wspm7vCIidTO9qd1gJBx+4OeOv961aNVztPOW2Kfv0kXVY/L53XzzaqhCvwsbChwT5W86t1f8vzAcizi5GhpZ2ipF8d8b96bc3kBhKi9NdjtpNGPmCeu3sTpiSybM1xFys+3LdqrBHfDc1iZE5pnBvNLHiwMrxgup/lJOILp3EYnlz6nVlE/eZrhxCKLBK2OwXzW78iQp/qjQcxUTFE4eNO9fL1f3GaO74VWteJAOjE8c2F2wqtPWkK/arJA7SSE+IgXoCstpIiRe3Yst3NO24PiaXh6oLCuLiWo2dmIzqV2idl7oAHF4Rs/YM1FnCdx2cI9c4Em+jsPVk4DOECB/M1tMHwjEwMxc1urLIDJFHonBW38MgH1Dp53tVyzLHpQVOaluWsb35WgCQb0AgwrdQ8WuiAay5fghvflVE+vcgvwY07R32z1cFsi7XjPvT1Kh2y4PP19j+59a22W6xQ+X0LmP06Y1YDl376FvTOi4K3/KQsPhzKeE/vP/cmC8YVEs45plMDmzBb59TFRnu2ivnad6wcGuvydzbrwUTWQ3CP89N9Vkv+O1p0W0abpqsXDkS7YqAUeWwlzQt9U3hJTsjtf3nzORrWfZug+PJw1GnF6u3B9klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwV4BR4lfi4N/WjRCzYHINT7ZXN8hTXghOFY2gKCrBEcdULviK7GAfh2w9PYCaBDrQ7lV/OUQd6YmRtxS7x2n9CQ==" + } + ] + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:CpSqkYHSG127MKJzBUwaQ5R3TyDDyWqgoqru0i0yoQg=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:3IwtFPxx+aQVediOcvQFqhRFznoqXnqFwUriKIPLEQ0=" + }, + "target": "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "randomness": "0", + "timestamp": 1721686870002, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAATBxQ0f5eSmAyq9GWCNaKVVjDJ2Wzo2gOY6BbsQoV5EaRTBIgVLLMG0XNxyFEdlXL+UNes0xfg1GpJzaDqK62Me+JDIgH5HCtjG6RT0ArFuGZlvDCK6CQYoZjFIpIJpNgmlBoRNgtzumF7P7rIc/GPKREIsL8HJrBDktJ1raBGToAmXuyJBWYk3Lumei8obOWqjzzS4nq5Wx78BYTZvQM85EXDFXru8mLk3wS/6KLxBuuXgFbMSdnnev6ifSaN0t14T51ihDKrYX3OKRp7LjvdD9cEmiKV1wNqG5KKFy0LkVdIzqYmnaFtNP3Lp1jzcR8KKZtRNJXTQ63yFPP1rDpKjQxA0n1YWEF9hIKJvcY1tHgZesWcY7FdwJsbC98fn9Z+1DJYq2LmppBb6+7JlgjNQhGNlcmsiZrgPbDD7iQYhUe+EWooR9OyLf1ujft2wGroIT4wO3M4p9cteMICS++INl2Y6zhsVnlNdwVghJLD0Kd0AQVkA6jxEB/beaSXOWdoOBWfhzPr+DC3A6JUeTfKTnjKvOrfTvf3WKfcJi83d0d07LymqWv2lZ+MmdaWrZ2k1xj9dSaLc/YFSYE+8gLwE8ZN5H2g5sbdVry4CpiIaQjX+nwCOScRklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwY20OS3MMRoRF0edV7MAYEiAI5WpkyFjofregIiLjmbMr0Q4VIHV8WrZjqSTiDrGBj3ygqruDvBIct8GPFLb1BA==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "AE8E39C81597A1C79984FC014137C09B70877A247B4390842A0884B495DD4258", + "noteCommitment": { + "type": "Buffer", + "data": "base64:PySdNCFehP11kb0hAHQG3yDASM2Ix95MGccF0yxnlkk=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:todu4D6yQZzV64lY1jA6qdA0t4CMmsQ0yrX8a0y13AM=" + }, + "target": "115792089237316195423570985008687907853269984665640564039457584007913129639935", + "randomness": "0", + "timestamp": 1721686870593, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAgjt4hfdcl/SHkDp2RESBhIWuTLaeXFVziFX6/6KCvKuF0mH+j4LlEQXGwyidBrOkqqYUapkpCMEGhLblIBZpq9HLd2JeOCcUyTvBgpuUc4ai+HNwuU6RQi6rjr9+ezH1lT1Jc5/AQoW2xjpgytpkTl+rjh5/l3JRl0K6UmjX3yIYOuuqVRDvZVXZ5PSEAvfnQgvaJ93N5FnM4YbxlimarF/f402E5jAQBKMn53Fv3OOK1MdU/AK5x7Hk6VHHa5DI1AM5Qi0kfPsfFw/rld66SLhX9jAhJtwnAizUIjnd0PK3oDFgo9aUOiaU3o4VXm6Xy7oERAaUukwyw876ApeQg0CdIEZM0YjzIzd18OlA/p/wkLnzSNA+YhmK6s6xcMJAiyY4ae231CH4K3GGc/eshZEcDU9Lb3MRpLYZB6fZwJMT4/Hv1SzkBQvoUvCNKNtDbN5HfSuBYY99oIXSZbEQ/DZ90IQ+8Ul6LK7HLZ626PhmtUZEn38Kss9DDWHKSnJIQ5kmX8IumJXDMfjOCIxkm0Y19JQX33i89xlSDebKVhH3zdhncFqaxPEJSSL7BLJIpQzN2pGEw+bLY6KA3Gzy2VPj1cK25D7uPNgXT/wR8h06ELjfClWItElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwKzXYdWoIVvs+eqBNNKdebAaImfAwueJBhVjohtuDDKwx+j4U/Mub0mIVhPQ+3En2wbFMLX/Z+vO4fqosUv0AAQ==" + } + ] + } ] } \ No newline at end of file diff --git a/ironfish/src/wallet/scanner/walletScanner.ts b/ironfish/src/wallet/scanner/walletScanner.ts index faeff67bf2..0f1b077eb3 100644 --- a/ironfish/src/wallet/scanner/walletScanner.ts +++ b/ironfish/src/wallet/scanner/walletScanner.ts @@ -234,6 +234,12 @@ export class WalletScanner { } await this.wallet.disconnectBlockForAccount(account, header, transactions) } + + for (const account of this.scanningAccounts) { + if (account.scanFrom && BufferUtils.equalsNullable(account.scanFrom.hash, header.hash)) { + account.scanFrom = null + } + } } getChainProcessor( From 65d8f58a6474cc199219bd697ac86245a704ded5 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Tue, 23 Jul 2024 18:52:43 -0700 Subject: [PATCH 68/81] ChainProcessor returns when head is not found (#5170) * ChainProcessor returns when head is not found * Added a test * Fixed lint --- ironfish/src/chainProcessor.test.ts | 14 ++++++++++++++ ironfish/src/chainProcessor.ts | 11 +++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/ironfish/src/chainProcessor.test.ts b/ironfish/src/chainProcessor.test.ts index e9b614bfe0..57da466006 100644 --- a/ironfish/src/chainProcessor.test.ts +++ b/ironfish/src/chainProcessor.test.ts @@ -198,4 +198,18 @@ describe('ChainProcessor', () => { expect(processor.hash).toEqualBuffer(block.header.hash) expect(onEvent).toHaveBeenCalledTimes(0) }) + + it('should not crash if head not found', async () => { + const MISSING_HEAD = Buffer.alloc(32, 'helloworld') + const processor = new ChainProcessor({ chain: nodeTest.chain, head: MISSING_HEAD }) + + const onEvent: Mock<(header: BlockHeader, event: 'add' | 'remove') => void> = jest.fn() + processor.onAdd.on((block) => onEvent(block, 'add')) + processor.onRemove.on((block) => onEvent(block, 'remove')) + + const result = await processor.update() + expect(result.hashChanged).toBe(false) + expect(processor.hash).toEqualBuffer(MISSING_HEAD) + expect(onEvent).toHaveBeenCalledTimes(0) + }) }) diff --git a/ironfish/src/chainProcessor.ts b/ironfish/src/chainProcessor.ts index 5958c39ad9..13a1ff4fcf 100644 --- a/ironfish/src/chainProcessor.ts +++ b/ironfish/src/chainProcessor.ts @@ -3,7 +3,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import type { Blockchain } from './blockchain' import type { BlockHeader } from './primitives' -import { Assert } from './assert' import { Event } from './event' import { createRootLogger, Logger } from './logger' @@ -73,14 +72,14 @@ export class ChainProcessor { const head = await this.chain.getHeader(this.hash) - Assert.isNotNull( - head, - `Chain processor head not found in chain: ${this.hash.toString('hex')}`, - ) - let blockCount = 0 let hashChanged = false + if (!head) { + this.logger.warn('ChainProcessor could not find head in blockchain.') + return { hashChanged } + } + const fork = await this.chain.findFork(head, chainHead) // All cases can be handled by rewinding to the fork point From fe7f55294e52871c9e7047f89b87cb446ca89381 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Tue, 23 Jul 2024 18:53:50 -0700 Subject: [PATCH 69/81] Fixes chain:rewind to properly rewind the wallet head (#5173) * Fix chain:rewind command to properly rewind the wallet * Fix chain:rewind --- ironfish-cli/src/commands/chain/rewind.ts | 83 +++++++++++--------- ironfish/src/wallet/scanner/scanState.ts | 8 +- ironfish/src/wallet/scanner/walletScanner.ts | 4 +- 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/ironfish-cli/src/commands/chain/rewind.ts b/ironfish-cli/src/commands/chain/rewind.ts index fb0a0ef150..c7bb2ebe7a 100644 --- a/ironfish-cli/src/commands/chain/rewind.ts +++ b/ironfish-cli/src/commands/chain/rewind.ts @@ -1,8 +1,8 @@ /* 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, Blockchain, FullNode, NodeUtils, Wallet } from '@ironfish/sdk' -import { Args, Command } from '@oclif/core' +import { Assert, Blockchain, BlockchainUtils, FullNode, NodeUtils, Wallet } from '@ironfish/sdk' +import { Args, Command, Flags } from '@oclif/core' import { IronfishCommand } from '../../command' import { LocalFlags } from '../../flags' import { ProgressBar, ProgressBarPresets } from '../../ui' @@ -13,11 +13,11 @@ export default class Rewind extends IronfishCommand { static hidden = true static args = { - to: Args.string({ + to: Args.integer({ required: true, description: 'The block sequence to rewind to', }), - from: Args.string({ + from: Args.integer({ required: false, description: 'The sequence to start removing blocks from', }), @@ -25,49 +25,58 @@ export default class Rewind extends IronfishCommand { static flags = { ...LocalFlags, + wallet: Flags.boolean({ + default: true, + allowNo: true, + description: 'should the wallet be rewinded', + }), } async start(): Promise { - const { args } = await this.parse(Rewind) + const { args, flags } = await this.parse(Rewind) const node = await this.sdk.node() await NodeUtils.waitForOpen(node) - await rewindChainTo(this, node, Number(args.to), Number(args.from)) + await rewindChainTo(this, node, flags.wallet, args.to, args.from) } } export const rewindChainTo = async ( command: Command, node: FullNode, + rewindWallet: boolean, to: number, from?: number, ): Promise => { const chain = node.chain const wallet = node.wallet - const sequence = to - - const fromSequence = from ? Math.max(from, chain.latest.sequence) : chain.latest.sequence + const { start, stop } = BlockchainUtils.getBlockRange(node.chain, { + start: to, + stop: from, + }) - const toDisconnect = fromSequence - sequence + const blockCount = stop - start - if (toDisconnect <= 0) { + if (blockCount <= 0) { command.log( - `Chain head currently at ${fromSequence}. Cannot rewind to ${sequence} because it is is greater than the latest sequence in the chain.`, + `Chain head currently at ${stop}. Cannot rewind to ${start} because it is is greater than the latest sequence in the chain.`, ) command.exit(1) } command.log( - `Chain currently has blocks up to ${fromSequence}. Rewinding ${toDisconnect} blocks to ${sequence}.`, + `Chain currently has blocks up to ${stop}. Rewinding ${blockCount} blocks to ${start}.`, ) - await disconnectBlocks(chain, toDisconnect) + await disconnectBlocks(chain, blockCount) - await rewindWalletHead(chain, wallet, sequence) + if (rewindWallet) { + await rewindWalletHead(chain, wallet) + } - await removeBlocks(chain, sequence, fromSequence) + await removeBlocks(chain, start, stop) } async function disconnectBlocks(chain: Blockchain, toDisconnect: number): Promise { @@ -92,34 +101,34 @@ async function disconnectBlocks(chain: Blockchain, toDisconnect: number): Promis bar.stop() } -async function rewindWalletHead( - chain: Blockchain, - wallet: Wallet, - sequence: number, -): Promise { - const latestHead = await wallet.getLatestHead() - - if (latestHead) { - const walletHead = await chain.getHeader(latestHead.hash) +async function rewindWalletHead(chain: Blockchain, wallet: Wallet): Promise { + const walletHead = await wallet.getLatestHead() - if (walletHead && walletHead.sequence > sequence) { - const bar = new ProgressBar('Rewinding wallet', { preset: ProgressBarPresets.withSpeed }) + if (!walletHead) { + return + } - const toRewind = walletHead.sequence - sequence - let rewound = 0 + if (walletHead.sequence > chain.head.sequence) { + const total = walletHead.sequence - chain.head.sequence - bar.start(toRewind, 0) + const bar = new ProgressBar('Rewinding wallet', { preset: ProgressBarPresets.withSpeed }) + bar.start(total, 0) - const scan = await wallet.scan({ wait: false }) + const scan = await wallet.scan({ wait: false }) - if (scan) { - scan.onTransaction.on((_) => { - bar.update(++rewound) - }) - } + if (scan) { + scan.onTransaction.on((sequence, _, action) => { + if (action === 'connect') { + bar.update(total - Math.abs(sequence - chain.head.sequence)) + } else { + bar.update(total - Math.abs(sequence - 1 - chain.head.sequence)) + } + }) - bar.stop() + await scan.wait() } + + bar.stop() } } diff --git a/ironfish/src/wallet/scanner/scanState.ts b/ironfish/src/wallet/scanner/scanState.ts index eda13da0a5..f79eccc1f4 100644 --- a/ironfish/src/wallet/scanner/scanState.ts +++ b/ironfish/src/wallet/scanner/scanState.ts @@ -16,7 +16,9 @@ export class ScanState { readonly end: HeadValue readonly startedAt: number readonly abortController: AbortController - readonly onTransaction = new Event<[sequence: number, endSequence: number]>() + readonly onTransaction = new Event< + [sequence: number, endSequence: number, action: 'connect' | 'disconnect'] + >() readonly speed = new Meter() private runningPromise: Promise @@ -43,11 +45,11 @@ export class ScanState { return (remaining / this.speed.rate1m) * 1000 } - signal(header: BlockHeader): void { + signal(header: BlockHeader, action: 'connect' | 'disconnect'): void { this.hash = header.hash this.sequence = header.sequence this.speed.add(1) - this.onTransaction.emit(header.sequence, this.end.sequence) + this.onTransaction.emit(header.sequence, this.end.sequence, action) } signalComplete(): void { diff --git a/ironfish/src/wallet/scanner/walletScanner.ts b/ironfish/src/wallet/scanner/walletScanner.ts index 0f1b077eb3..64e74126cd 100644 --- a/ironfish/src/wallet/scanner/walletScanner.ts +++ b/ironfish/src/wallet/scanner/walletScanner.ts @@ -89,12 +89,12 @@ export class WalletScanner { chainProcessor.onAdd.on(async ({ header, transactions }) => { await this.connectBlock(header, transactions, decryptor, this.state?.abortController) - this.state?.signal(header) + this.state?.signal(header, 'connect') }) chainProcessor.onRemove.on(async ({ header, transactions }) => { await this.disconnectBlock(header, transactions, this.state?.abortController) - this.state?.signal(header) + this.state?.signal(header, 'disconnect') }) // Once we set up ChainProcessor, if the start is null we want to use From 04d941bd68984791e515c158ae75648eb3c1a73f Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Wed, 24 Jul 2024 10:17:05 -0700 Subject: [PATCH 70/81] Add the ability for CLI commands to track and close an RPC client (#5172) * Add the ability for CLI commands to track and close an RPC client With the addition of the JSON changes, which requires us to await on the start command in the IronfishCommand, commands that used `this.sdk.connectRpc()` were hanging because the clients were not being closed. This change allows us to fetch a client from the IronfishCommand directly, which gives the IronfishCommand the ability to track the client, and close it when the code is done being ran/executed. * close connections in the finally block * remove unneeded return --- ironfish-cli/src/command.ts | 11 +++++++++++ ironfish-cli/src/commands/chain/assets/info.ts | 2 +- ironfish-cli/src/commands/chain/blocks/info.ts | 2 +- ironfish-cli/src/commands/chain/broadcast.ts | 2 +- ironfish-cli/src/commands/chain/export.ts | 2 +- ironfish-cli/src/commands/chain/power.ts | 2 +- ironfish-cli/src/commands/chain/status.ts | 2 +- ironfish-cli/src/commands/chain/transactions/info.ts | 2 +- ironfish-cli/src/commands/config/edit.ts | 2 +- ironfish-cli/src/commands/config/get.ts | 2 +- ironfish-cli/src/commands/config/index.ts | 2 +- ironfish-cli/src/commands/config/set.ts | 2 +- ironfish-cli/src/commands/config/unset.ts | 2 +- ironfish-cli/src/commands/faucet.ts | 2 +- ironfish-cli/src/commands/fees.ts | 2 +- ironfish-cli/src/commands/mempool/status.ts | 2 +- ironfish-cli/src/commands/miners/start.ts | 2 +- ironfish-cli/src/commands/rpc/status.ts | 2 +- ironfish-cli/src/commands/status.ts | 2 +- ironfish-cli/src/commands/wallet/accounts.ts | 2 +- ironfish-cli/src/commands/wallet/address.ts | 2 +- ironfish-cli/src/commands/wallet/assets.ts | 2 +- ironfish-cli/src/commands/wallet/balance.ts | 2 +- ironfish-cli/src/commands/wallet/balances.ts | 2 +- ironfish-cli/src/commands/wallet/burn.ts | 2 +- ironfish-cli/src/commands/wallet/chainport/send.ts | 2 +- ironfish-cli/src/commands/wallet/create.ts | 2 +- ironfish-cli/src/commands/wallet/delete.ts | 2 +- ironfish-cli/src/commands/wallet/export.ts | 2 +- ironfish-cli/src/commands/wallet/import.ts | 2 +- ironfish-cli/src/commands/wallet/mint.ts | 2 +- .../commands/wallet/multisig/account/participants.ts | 2 +- .../commands/wallet/multisig/commitment/aggregate.ts | 2 +- .../src/commands/wallet/multisig/commitment/create.ts | 2 +- .../src/commands/wallet/multisig/dealer/create.ts | 2 +- .../src/commands/wallet/multisig/dkg/round1.ts | 2 +- .../src/commands/wallet/multisig/dkg/round2.ts | 2 +- .../src/commands/wallet/multisig/dkg/round3.ts | 2 +- .../commands/wallet/multisig/participant/create.ts | 2 +- .../src/commands/wallet/multisig/participant/index.ts | 2 +- .../commands/wallet/multisig/participants/index.ts | 2 +- .../commands/wallet/multisig/signature/aggregate.ts | 2 +- .../src/commands/wallet/multisig/signature/create.ts | 2 +- ironfish-cli/src/commands/wallet/notes/combine.ts | 2 +- ironfish-cli/src/commands/wallet/notes/index.ts | 2 +- ironfish-cli/src/commands/wallet/post.ts | 2 +- ironfish-cli/src/commands/wallet/rename.ts | 2 +- ironfish-cli/src/commands/wallet/rescan.ts | 2 +- ironfish-cli/src/commands/wallet/reset.ts | 2 +- ironfish-cli/src/commands/wallet/scanning/off.ts | 2 +- ironfish-cli/src/commands/wallet/scanning/on.ts | 2 +- ironfish-cli/src/commands/wallet/send.ts | 2 +- ironfish-cli/src/commands/wallet/sign.ts | 2 +- ironfish-cli/src/commands/wallet/status.ts | 2 +- .../src/commands/wallet/transaction/import.ts | 2 +- ironfish-cli/src/commands/wallet/transaction/index.ts | 2 +- ironfish-cli/src/commands/wallet/transaction/view.ts | 2 +- ironfish-cli/src/commands/wallet/transaction/watch.ts | 2 +- ironfish-cli/src/commands/wallet/transactions.ts | 2 +- ironfish-cli/src/commands/wallet/use.ts | 2 +- ironfish-cli/src/commands/wallet/which.ts | 2 +- ironfish-cli/src/commands/workers/status.ts | 2 +- ironfish/src/rpc/clients/client.ts | 2 ++ ironfish/src/rpc/clients/memoryClient.ts | 2 ++ 64 files changed, 76 insertions(+), 61 deletions(-) diff --git a/ironfish-cli/src/command.ts b/ironfish-cli/src/command.ts index 0534adc29e..67086d6c0e 100644 --- a/ironfish-cli/src/command.ts +++ b/ironfish-cli/src/command.ts @@ -9,6 +9,7 @@ import { InternalOptions, IronfishSdk, Logger, + RpcClient, RpcConnectionError, } from '@ironfish/sdk' import { Command, Config, ux } from '@oclif/core' @@ -69,6 +70,8 @@ export abstract class IronfishCommand extends Command { */ closing = false + client: RpcClient | null = null + constructor(argv: string[], config: Config) { super(argv, config) this.logger = createRootLogger().withTag(this.ctor.id) @@ -104,6 +107,8 @@ export abstract class IronfishCommand extends Command { } else { throw error } + } finally { + this.client?.close() } this.exit(0) @@ -225,6 +230,12 @@ export abstract class IronfishCommand extends Command { logJson(json: unknown): void { ux.stdout(ui.json(json)) } + + async connectRpc(forceLocal = false, forceRemote = false): Promise { + const client = await this.sdk.connectRpc(forceLocal, forceRemote) + this.client = client + return client + } } function getFlag(flags: unknown, flag: FLAGS): unknown { diff --git a/ironfish-cli/src/commands/chain/assets/info.ts b/ironfish-cli/src/commands/chain/assets/info.ts index f9b0c18be6..d30dccf669 100644 --- a/ironfish-cli/src/commands/chain/assets/info.ts +++ b/ironfish-cli/src/commands/chain/assets/info.ts @@ -25,7 +25,7 @@ export default class AssetInfo extends IronfishCommand { const { args } = await this.parse(AssetInfo) const { id: assetId } = args - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const data = await client.chain.getAsset({ id: assetId }) this.log( diff --git a/ironfish-cli/src/commands/chain/blocks/info.ts b/ironfish-cli/src/commands/chain/blocks/info.ts index f836e924d9..ca30eed467 100644 --- a/ironfish-cli/src/commands/chain/blocks/info.ts +++ b/ironfish-cli/src/commands/chain/blocks/info.ts @@ -27,7 +27,7 @@ export default class BlockInfo extends IronfishCommand { const { args } = await this.parse(BlockInfo) const { search } = args - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const data = await client.chain.getBlock({ search }) const blockData = data.content diff --git a/ironfish-cli/src/commands/chain/broadcast.ts b/ironfish-cli/src/commands/chain/broadcast.ts index 8e2c5e644e..9d2bb42032 100644 --- a/ironfish-cli/src/commands/chain/broadcast.ts +++ b/ironfish-cli/src/commands/chain/broadcast.ts @@ -24,7 +24,7 @@ export class BroadcastCommand extends IronfishCommand { const { transaction } = args ux.action.start(`Broadcasting transaction`) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.chain.broadcastTransaction({ transaction }) if (response.content) { ux.action.stop(`Transaction broadcasted: ${response.content.hash}`) diff --git a/ironfish-cli/src/commands/chain/export.ts b/ironfish-cli/src/commands/chain/export.ts index 0ba247ec35..129d25aaed 100644 --- a/ironfish-cli/src/commands/chain/export.ts +++ b/ironfish-cli/src/commands/chain/export.ts @@ -41,7 +41,7 @@ export default class Export extends IronfishCommand { const exportPath = this.sdk.fileSystem.join(exportDir, 'data.json') - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const stream = client.chain.exportChainStream({ start: args.start, diff --git a/ironfish-cli/src/commands/chain/power.ts b/ironfish-cli/src/commands/chain/power.ts index 72bc2a8ac4..b551ac0cbf 100644 --- a/ironfish-cli/src/commands/chain/power.ts +++ b/ironfish-cli/src/commands/chain/power.ts @@ -29,7 +29,7 @@ export default class Power extends IronfishCommand { const { flags, args } = await this.parse(Power) const { block } = args - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const data = await client.chain.getNetworkHashPower({ sequence: block, diff --git a/ironfish-cli/src/commands/chain/status.ts b/ironfish-cli/src/commands/chain/status.ts index 0bfd9c1f45..a560f23b04 100644 --- a/ironfish-cli/src/commands/chain/status.ts +++ b/ironfish-cli/src/commands/chain/status.ts @@ -14,7 +14,7 @@ export default class ChainStatus extends IronfishCommand { } async start(): Promise { - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const [status, difficulty, power] = await Promise.all([ client.node.getStatus(), diff --git a/ironfish-cli/src/commands/chain/transactions/info.ts b/ironfish-cli/src/commands/chain/transactions/info.ts index d022987af4..21c301205c 100644 --- a/ironfish-cli/src/commands/chain/transactions/info.ts +++ b/ironfish-cli/src/commands/chain/transactions/info.ts @@ -25,7 +25,7 @@ export class TransactionInfo extends IronfishCommand { async start(): Promise { const { args } = await this.parse(TransactionInfo) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.chain.getTransaction({ transactionHash: args.hash, diff --git a/ironfish-cli/src/commands/config/edit.ts b/ironfish-cli/src/commands/config/edit.ts index 48cb99002c..5b951f6c93 100644 --- a/ironfish-cli/src/commands/config/edit.ts +++ b/ironfish-cli/src/commands/config/edit.ts @@ -39,7 +39,7 @@ export class EditCommand extends IronfishCommand { this.exit(code || undefined) } - const client = await this.sdk.connectRpc(!flags.remote) + const client = await this.connectRpc(!flags.remote) const response = await client.config.getConfig({ user: true }) const output = JSON.stringify(response.content, undefined, ' ') diff --git a/ironfish-cli/src/commands/config/get.ts b/ironfish-cli/src/commands/config/get.ts index 9d2c16dd5a..47f83275ad 100644 --- a/ironfish-cli/src/commands/config/get.ts +++ b/ironfish-cli/src/commands/config/get.ts @@ -42,7 +42,7 @@ export class GetCommand extends IronfishCommand { const { args, flags } = await this.parse(GetCommand) const { name } = args - const client = await this.sdk.connectRpc(flags.local) + const client = await this.connectRpc(flags.local) const response = await client.config.getConfig({ user: flags.user, diff --git a/ironfish-cli/src/commands/config/index.ts b/ironfish-cli/src/commands/config/index.ts index c7beba2598..49995959f1 100644 --- a/ironfish-cli/src/commands/config/index.ts +++ b/ironfish-cli/src/commands/config/index.ts @@ -25,7 +25,7 @@ export class ShowCommand extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(ShowCommand) - const client = await this.sdk.connectRpc(flags.local) + const client = await this.connectRpc(flags.local) const response = await client.config.getConfig({ user: flags.user }) let output = JSON.stringify(response.content, undefined, ' ') diff --git a/ironfish-cli/src/commands/config/set.ts b/ironfish-cli/src/commands/config/set.ts index 906b0dacca..5cd83a7e31 100644 --- a/ironfish-cli/src/commands/config/set.ts +++ b/ironfish-cli/src/commands/config/set.ts @@ -35,7 +35,7 @@ export class SetCommand extends IronfishCommand { const { args, flags } = await this.parse(SetCommand) const { name, value } = args - const client = await this.sdk.connectRpc(flags.local) + const client = await this.connectRpc(flags.local) await client.config.setConfig({ name, value }) this.exit(0) diff --git a/ironfish-cli/src/commands/config/unset.ts b/ironfish-cli/src/commands/config/unset.ts index d28a549682..3dc81ecd12 100644 --- a/ironfish-cli/src/commands/config/unset.ts +++ b/ironfish-cli/src/commands/config/unset.ts @@ -29,7 +29,7 @@ export class UnsetCommand extends IronfishCommand { const { args, flags } = await this.parse(UnsetCommand) const { name } = args - const client = await this.sdk.connectRpc(flags.local) + const client = await this.connectRpc(flags.local) await client.config.unsetConfig({ name }) this.exit(0) diff --git a/ironfish-cli/src/commands/faucet.ts b/ironfish-cli/src/commands/faucet.ts index d4fde5c27d..286c9cd3d0 100644 --- a/ironfish-cli/src/commands/faucet.ts +++ b/ironfish-cli/src/commands/faucet.ts @@ -34,7 +34,7 @@ export class FaucetCommand extends IronfishCommand { this.exit(1) } - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const networkInfoResponse = await client.chain.getNetworkInfo() if (networkInfoResponse.content === null || networkInfoResponse.content.networkId !== 0) { diff --git a/ironfish-cli/src/commands/fees.ts b/ironfish-cli/src/commands/fees.ts index bebd774d7b..fa3b5f073b 100644 --- a/ironfish-cli/src/commands/fees.ts +++ b/ironfish-cli/src/commands/fees.ts @@ -26,7 +26,7 @@ export class FeeCommand extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(FeeCommand) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() if (flags.explain) { await this.explainFeeRates(client) diff --git a/ironfish-cli/src/commands/mempool/status.ts b/ironfish-cli/src/commands/mempool/status.ts index 740b61892d..14bb320e1d 100644 --- a/ironfish-cli/src/commands/mempool/status.ts +++ b/ironfish-cli/src/commands/mempool/status.ts @@ -25,7 +25,7 @@ export default class Status extends IronfishCommand { const { flags } = await this.parse(Status) if (!flags.follow) { - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.mempool.getMempoolStatus() this.log(renderStatus(response.content)) this.exit(0) diff --git a/ironfish-cli/src/commands/miners/start.ts b/ironfish-cli/src/commands/miners/start.ts index 2ec9fc4f1e..29ad236df5 100644 --- a/ironfish-cli/src/commands/miners/start.ts +++ b/ironfish-cli/src/commands/miners/start.ts @@ -81,7 +81,7 @@ export class Miner extends IronfishCommand { let publicAddress = flags.address if (publicAddress == null) { - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const publicKeyResponse = await client.wallet.getAccountPublicKey() publicAddress = publicKeyResponse.content.publicKey diff --git a/ironfish-cli/src/commands/rpc/status.ts b/ironfish-cli/src/commands/rpc/status.ts index 0ef3fdac11..cd4e3ffe2a 100644 --- a/ironfish-cli/src/commands/rpc/status.ts +++ b/ironfish-cli/src/commands/rpc/status.ts @@ -24,7 +24,7 @@ export default class Status extends IronfishCommand { const { flags } = await this.parse(Status) if (!flags.follow) { - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.rpc.getRpcStatus() this.log(renderStatus(response.content)) this.exit(0) diff --git a/ironfish-cli/src/commands/status.ts b/ironfish-cli/src/commands/status.ts index f341cfe3d6..14e9013457 100644 --- a/ironfish-cli/src/commands/status.ts +++ b/ironfish-cli/src/commands/status.ts @@ -35,7 +35,7 @@ export default class Status extends IronfishCommand { const { flags } = await this.parse(Status) if (!flags.follow) { - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.node.getStatus() this.log(renderStatus(response.content, flags.all)) this.exit(0) diff --git a/ironfish-cli/src/commands/wallet/accounts.ts b/ironfish-cli/src/commands/wallet/accounts.ts index 1439c2c626..9b14f58d28 100644 --- a/ironfish-cli/src/commands/wallet/accounts.ts +++ b/ironfish-cli/src/commands/wallet/accounts.ts @@ -19,7 +19,7 @@ export class AccountsCommand extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(AccountsCommand) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.wallet.getAccounts({ displayName: flags.displayName }) diff --git a/ironfish-cli/src/commands/wallet/address.ts b/ironfish-cli/src/commands/wallet/address.ts index 0d2e0f8e14..e555c689e5 100644 --- a/ironfish-cli/src/commands/wallet/address.ts +++ b/ironfish-cli/src/commands/wallet/address.ts @@ -25,7 +25,7 @@ export class AddressCommand extends IronfishCommand { const { args } = await this.parse(AddressCommand) const { account } = args - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.wallet.getAccountPublicKey({ account: account, diff --git a/ironfish-cli/src/commands/wallet/assets.ts b/ironfish-cli/src/commands/wallet/assets.ts index e7edec57f9..b21fcfb779 100644 --- a/ironfish-cli/src/commands/wallet/assets.ts +++ b/ironfish-cli/src/commands/wallet/assets.ts @@ -46,7 +46,7 @@ export class AssetsCommand extends IronfishCommand { // TODO: remove account arg const account = flags.account ? flags.account : args.account - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = client.wallet.getAssets({ account, }) diff --git a/ironfish-cli/src/commands/wallet/balance.ts b/ironfish-cli/src/commands/wallet/balance.ts index bc2a69bbbe..4e74cf5800 100644 --- a/ironfish-cli/src/commands/wallet/balance.ts +++ b/ironfish-cli/src/commands/wallet/balance.ts @@ -51,7 +51,7 @@ export class BalanceCommand extends IronfishCommand { // TODO: remove account arg const account = flags.account ? flags.account : args.account - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.wallet.getAccountBalance({ account, diff --git a/ironfish-cli/src/commands/wallet/balances.ts b/ironfish-cli/src/commands/wallet/balances.ts index 5a3386e7e6..ca96906f1f 100644 --- a/ironfish-cli/src/commands/wallet/balances.ts +++ b/ironfish-cli/src/commands/wallet/balances.ts @@ -39,7 +39,7 @@ export class BalancesCommand extends IronfishCommand { async start(): Promise { const { flags, args } = await this.parse(BalancesCommand) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() // TODO: remove account arg const account = flags.account ? flags.account : args.account diff --git a/ironfish-cli/src/commands/wallet/burn.ts b/ironfish-cli/src/commands/wallet/burn.ts index 7754011ee6..5827df6420 100644 --- a/ironfish-cli/src/commands/wallet/burn.ts +++ b/ironfish-cli/src/commands/wallet/burn.ts @@ -95,7 +95,7 @@ export class Burn extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(Burn) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() if (!flags.offline) { const status = await client.wallet.getNodeStatus() diff --git a/ironfish-cli/src/commands/wallet/chainport/send.ts b/ironfish-cli/src/commands/wallet/chainport/send.ts index 17dfa6ddd8..b613651778 100644 --- a/ironfish-cli/src/commands/wallet/chainport/send.ts +++ b/ironfish-cli/src/commands/wallet/chainport/send.ts @@ -86,7 +86,7 @@ export class BridgeCommand extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(BridgeCommand) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const networkId = (await client.chain.getNetworkInfo()).content.networkId diff --git a/ironfish-cli/src/commands/wallet/create.ts b/ironfish-cli/src/commands/wallet/create.ts index dc17c19de3..fcb9758c6c 100644 --- a/ironfish-cli/src/commands/wallet/create.ts +++ b/ironfish-cli/src/commands/wallet/create.ts @@ -29,7 +29,7 @@ export class CreateCommand extends IronfishCommand { name = await inputPrompt('Enter the name of the account', true) } - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() this.log(`Creating account ${name}`) const result = await client.wallet.createAccount({ name }) diff --git a/ironfish-cli/src/commands/wallet/delete.ts b/ironfish-cli/src/commands/wallet/delete.ts index 1248541ab5..7aa7569a54 100644 --- a/ironfish-cli/src/commands/wallet/delete.ts +++ b/ironfish-cli/src/commands/wallet/delete.ts @@ -33,7 +33,7 @@ export class DeleteCommand extends IronfishCommand { const { confirm, wait } = flags const { account } = args - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() ux.action.start(`Deleting account '${account}'`) const response = await client.wallet.removeAccount({ account, confirm, wait }) diff --git a/ironfish-cli/src/commands/wallet/export.ts b/ironfish-cli/src/commands/wallet/export.ts index b48e363fd7..3a271fef62 100644 --- a/ironfish-cli/src/commands/wallet/export.ts +++ b/ironfish-cli/src/commands/wallet/export.ts @@ -65,7 +65,7 @@ export class ExportCommand extends IronfishCommand { ? AccountFormat.JSON : AccountFormat.Base64Json - const client = await this.sdk.connectRpc(local) + const client = await this.connectRpc(local) const response = await client.wallet.exportAccount({ account, viewOnly, diff --git a/ironfish-cli/src/commands/wallet/import.ts b/ironfish-cli/src/commands/wallet/import.ts index 9c0b1ffa1b..535e5eaa11 100644 --- a/ironfish-cli/src/commands/wallet/import.ts +++ b/ironfish-cli/src/commands/wallet/import.ts @@ -51,7 +51,7 @@ export class ImportCommand extends IronfishCommand { const { flags, args } = await this.parse(ImportCommand) const { blob } = args - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() let account: string diff --git a/ironfish-cli/src/commands/wallet/mint.ts b/ironfish-cli/src/commands/wallet/mint.ts index 86e7519c25..0557493128 100644 --- a/ironfish-cli/src/commands/wallet/mint.ts +++ b/ironfish-cli/src/commands/wallet/mint.ts @@ -116,7 +116,7 @@ export class Mint extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(Mint) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() if (!flags.offline) { const status = await client.wallet.getNodeStatus() diff --git a/ironfish-cli/src/commands/wallet/multisig/account/participants.ts b/ironfish-cli/src/commands/wallet/multisig/account/participants.ts index 80e11ef213..eca3361fc9 100644 --- a/ironfish-cli/src/commands/wallet/multisig/account/participants.ts +++ b/ironfish-cli/src/commands/wallet/multisig/account/participants.ts @@ -19,7 +19,7 @@ export class MultisigAccountParticipants extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(MultisigAccountParticipants) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.wallet.multisig.getAccountIdentities({ account: flags.account, diff --git a/ironfish-cli/src/commands/wallet/multisig/commitment/aggregate.ts b/ironfish-cli/src/commands/wallet/multisig/commitment/aggregate.ts index 5c70c2952b..f09021af7c 100644 --- a/ironfish-cli/src/commands/wallet/multisig/commitment/aggregate.ts +++ b/ironfish-cli/src/commands/wallet/multisig/commitment/aggregate.ts @@ -55,7 +55,7 @@ export class CreateSigningPackage extends IronfishCommand { } commitments = commitments.map((s) => s.trim()) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const signingPackageResponse = await client.wallet.multisig.createSigningPackage({ account: flags.account, diff --git a/ironfish-cli/src/commands/wallet/multisig/commitment/create.ts b/ironfish-cli/src/commands/wallet/multisig/commitment/create.ts index f307a15c4f..fff272cd81 100644 --- a/ironfish-cli/src/commands/wallet/multisig/commitment/create.ts +++ b/ironfish-cli/src/commands/wallet/multisig/commitment/create.ts @@ -69,7 +69,7 @@ export class CreateSigningCommitmentCommand extends IronfishCommand { }) } - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const unsignedTransaction = new UnsignedTransaction( Buffer.from(unsignedTransactionInput, 'hex'), ) diff --git a/ironfish-cli/src/commands/wallet/multisig/dealer/create.ts b/ironfish-cli/src/commands/wallet/multisig/dealer/create.ts index f0283cdd90..1a41a274ca 100644 --- a/ironfish-cli/src/commands/wallet/multisig/dealer/create.ts +++ b/ironfish-cli/src/commands/wallet/multisig/dealer/create.ts @@ -62,7 +62,7 @@ export class MultisigCreateDealer extends IronfishCommand { } } - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const name = await this.getCoordinatorName(client, flags.name?.trim()) diff --git a/ironfish-cli/src/commands/wallet/multisig/dkg/round1.ts b/ironfish-cli/src/commands/wallet/multisig/dkg/round1.ts index 08a2c03eca..d03f4a06f8 100644 --- a/ironfish-cli/src/commands/wallet/multisig/dkg/round1.ts +++ b/ironfish-cli/src/commands/wallet/multisig/dkg/round1.ts @@ -33,7 +33,7 @@ export class DkgRound1Command extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(DkgRound1Command) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() let participantName = flags.participantName if (!participantName) { diff --git a/ironfish-cli/src/commands/wallet/multisig/dkg/round2.ts b/ironfish-cli/src/commands/wallet/multisig/dkg/round2.ts index 8e8c1fe3f7..ebc0f409c6 100644 --- a/ironfish-cli/src/commands/wallet/multisig/dkg/round2.ts +++ b/ironfish-cli/src/commands/wallet/multisig/dkg/round2.ts @@ -33,7 +33,7 @@ export class DkgRound2Command extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(DkgRound2Command) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() let participantName = flags.participantName if (!participantName) { diff --git a/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts b/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts index 999cb280c2..7cb163bcd3 100644 --- a/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts +++ b/ironfish-cli/src/commands/wallet/multisig/dkg/round3.ts @@ -43,7 +43,7 @@ export class DkgRound3Command extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(DkgRound3Command) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() let participantName = flags.participantName if (!participantName) { diff --git a/ironfish-cli/src/commands/wallet/multisig/participant/create.ts b/ironfish-cli/src/commands/wallet/multisig/participant/create.ts index f0499a670a..5357cfcfc9 100644 --- a/ironfish-cli/src/commands/wallet/multisig/participant/create.ts +++ b/ironfish-cli/src/commands/wallet/multisig/participant/create.ts @@ -25,7 +25,7 @@ export class MultisigIdentityCreate extends IronfishCommand { name = await inputPrompt('Enter a name for the identity', true) } - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() let response while (!response) { try { diff --git a/ironfish-cli/src/commands/wallet/multisig/participant/index.ts b/ironfish-cli/src/commands/wallet/multisig/participant/index.ts index 7fa6698989..58241fcf41 100644 --- a/ironfish-cli/src/commands/wallet/multisig/participant/index.ts +++ b/ironfish-cli/src/commands/wallet/multisig/participant/index.ts @@ -20,7 +20,7 @@ export class MultisigIdentity extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(MultisigIdentity) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() if (flags.name) { const response = await client.wallet.multisig.getIdentity({ name: flags.name }) diff --git a/ironfish-cli/src/commands/wallet/multisig/participants/index.ts b/ironfish-cli/src/commands/wallet/multisig/participants/index.ts index a765401409..806e499a28 100644 --- a/ironfish-cli/src/commands/wallet/multisig/participants/index.ts +++ b/ironfish-cli/src/commands/wallet/multisig/participants/index.ts @@ -13,7 +13,7 @@ export class MultisigParticipants extends IronfishCommand { } async start(): Promise { - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.wallet.multisig.getIdentities() const participants = [] diff --git a/ironfish-cli/src/commands/wallet/multisig/signature/aggregate.ts b/ironfish-cli/src/commands/wallet/multisig/signature/aggregate.ts index 53791127d3..7d22da5913 100644 --- a/ironfish-cli/src/commands/wallet/multisig/signature/aggregate.ts +++ b/ironfish-cli/src/commands/wallet/multisig/signature/aggregate.ts @@ -64,7 +64,7 @@ export class MultisigSign extends IronfishCommand { ux.action.start('Signing the multisig transaction') - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.wallet.multisig.aggregateSignatureShares({ account: flags.account, diff --git a/ironfish-cli/src/commands/wallet/multisig/signature/create.ts b/ironfish-cli/src/commands/wallet/multisig/signature/create.ts index 5d06bced33..39812cec25 100644 --- a/ironfish-cli/src/commands/wallet/multisig/signature/create.ts +++ b/ironfish-cli/src/commands/wallet/multisig/signature/create.ts @@ -46,7 +46,7 @@ export class CreateSignatureShareCommand extends IronfishCommand { signingPackageString = await longPrompt('Enter the signing package') } - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const signingPackage = new multisig.SigningPackage(Buffer.from(signingPackageString, 'hex')) const unsignedTransaction = new UnsignedTransaction( diff --git a/ironfish-cli/src/commands/wallet/notes/combine.ts b/ironfish-cli/src/commands/wallet/notes/combine.ts index f6c34097d0..c48a31dd61 100644 --- a/ironfish-cli/src/commands/wallet/notes/combine.ts +++ b/ironfish-cli/src/commands/wallet/notes/combine.ts @@ -218,7 +218,7 @@ export class CombineNotesCommand extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(CombineNotesCommand) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() let to = flags.to let from = flags.account diff --git a/ironfish-cli/src/commands/wallet/notes/index.ts b/ironfish-cli/src/commands/wallet/notes/index.ts index ec99ddee2b..a00d62efce 100644 --- a/ironfish-cli/src/commands/wallet/notes/index.ts +++ b/ironfish-cli/src/commands/wallet/notes/index.ts @@ -35,7 +35,7 @@ export class NotesCommand extends IronfishCommand { const assetLookup: Map = new Map() - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = client.wallet.getAccountNotesStream({ account }) diff --git a/ironfish-cli/src/commands/wallet/post.ts b/ironfish-cli/src/commands/wallet/post.ts index 0d31888ba3..8ff40c6322 100644 --- a/ironfish-cli/src/commands/wallet/post.ts +++ b/ironfish-cli/src/commands/wallet/post.ts @@ -57,7 +57,7 @@ export class PostCommand extends IronfishCommand { const serialized = Buffer.from(transaction, 'hex') const raw = RawTransactionSerde.deserialize(serialized) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const senderAddress = raw.sender() if (!senderAddress) { diff --git a/ironfish-cli/src/commands/wallet/rename.ts b/ironfish-cli/src/commands/wallet/rename.ts index 18da1aa716..224913fe3c 100644 --- a/ironfish-cli/src/commands/wallet/rename.ts +++ b/ironfish-cli/src/commands/wallet/rename.ts @@ -27,7 +27,7 @@ export class RenameCommand extends IronfishCommand { const { args } = await this.parse(RenameCommand) const { account, newName } = args - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() await client.wallet.renameAccount({ account, newName }) this.log(`Account ${account} renamed to ${newName}`) } diff --git a/ironfish-cli/src/commands/wallet/rescan.ts b/ironfish-cli/src/commands/wallet/rescan.ts index 9467347fd5..3761dbb22c 100644 --- a/ironfish-cli/src/commands/wallet/rescan.ts +++ b/ironfish-cli/src/commands/wallet/rescan.ts @@ -34,7 +34,7 @@ export class RescanCommand extends IronfishCommand { this.error('You cannot pass both --local and --no-follow') } - const client = await this.sdk.connectRpc(local) + const client = await this.connectRpc(local) ux.action.start('Asking node to start scanning', undefined, { stdout: true, diff --git a/ironfish-cli/src/commands/wallet/reset.ts b/ironfish-cli/src/commands/wallet/reset.ts index b4af982e39..b7e57e3e7c 100644 --- a/ironfish-cli/src/commands/wallet/reset.ts +++ b/ironfish-cli/src/commands/wallet/reset.ts @@ -45,7 +45,7 @@ export class ResetCommand extends IronfishCommand { flags.confirm, ) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() await client.wallet.resetAccount({ account, diff --git a/ironfish-cli/src/commands/wallet/scanning/off.ts b/ironfish-cli/src/commands/wallet/scanning/off.ts index 22fa58966c..7dc843fd31 100644 --- a/ironfish-cli/src/commands/wallet/scanning/off.ts +++ b/ironfish-cli/src/commands/wallet/scanning/off.ts @@ -23,7 +23,7 @@ export class ScanningOffCommand extends IronfishCommand { const { args } = await this.parse(ScanningOffCommand) const { account } = args - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() await client.wallet.setScanning({ account: account, diff --git a/ironfish-cli/src/commands/wallet/scanning/on.ts b/ironfish-cli/src/commands/wallet/scanning/on.ts index f4e5053236..58339bdd66 100644 --- a/ironfish-cli/src/commands/wallet/scanning/on.ts +++ b/ironfish-cli/src/commands/wallet/scanning/on.ts @@ -23,7 +23,7 @@ export class ScanningOnCommand extends IronfishCommand { const { args } = await this.parse(ScanningOnCommand) const { account } = args - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() await client.wallet.setScanning({ account: account, diff --git a/ironfish-cli/src/commands/wallet/send.ts b/ironfish-cli/src/commands/wallet/send.ts index 70690812e9..fb442012be 100644 --- a/ironfish-cli/src/commands/wallet/send.ts +++ b/ironfish-cli/src/commands/wallet/send.ts @@ -124,7 +124,7 @@ export class Send extends IronfishCommand { let to = flags.to let from = flags.account - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() if (!flags.offline) { const status = await client.wallet.getNodeStatus() diff --git a/ironfish-cli/src/commands/wallet/sign.ts b/ironfish-cli/src/commands/wallet/sign.ts index 354ece2177..5c80ca0935 100644 --- a/ironfish-cli/src/commands/wallet/sign.ts +++ b/ironfish-cli/src/commands/wallet/sign.ts @@ -35,7 +35,7 @@ export class SignTransaction extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(SignTransaction) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() if (!flags.broadcast && flags.watch) { this.error('Cannot use --watch without --broadcast') diff --git a/ironfish-cli/src/commands/wallet/status.ts b/ironfish-cli/src/commands/wallet/status.ts index 30b3cb8c96..ad53d7d637 100644 --- a/ironfish-cli/src/commands/wallet/status.ts +++ b/ironfish-cli/src/commands/wallet/status.ts @@ -17,7 +17,7 @@ export class StatusCommand extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(StatusCommand) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.wallet.getAccountsStatus() diff --git a/ironfish-cli/src/commands/wallet/transaction/import.ts b/ironfish-cli/src/commands/wallet/transaction/import.ts index 0baef7c2d5..e556007ca3 100644 --- a/ironfish-cli/src/commands/wallet/transaction/import.ts +++ b/ironfish-cli/src/commands/wallet/transaction/import.ts @@ -57,7 +57,7 @@ export class TransactionImportCommand extends IronfishCommand { } ux.action.start(`Importing transaction`) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.wallet.addTransaction({ transaction, broadcast: flags.broadcast, diff --git a/ironfish-cli/src/commands/wallet/transaction/index.ts b/ironfish-cli/src/commands/wallet/transaction/index.ts index f20d0dd899..a55b5c6ef4 100644 --- a/ironfish-cli/src/commands/wallet/transaction/index.ts +++ b/ironfish-cli/src/commands/wallet/transaction/index.ts @@ -49,7 +49,7 @@ export class TransactionCommand extends IronfishCommand { // TODO: remove account arg const account = flags.account ? flags.account : args.account - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const networkId = (await client.chain.getNetworkInfo()).content.networkId const response = await client.wallet.getAccountTransaction({ diff --git a/ironfish-cli/src/commands/wallet/transaction/view.ts b/ironfish-cli/src/commands/wallet/transaction/view.ts index 7f13d96f05..6ac2e81332 100644 --- a/ironfish-cli/src/commands/wallet/transaction/view.ts +++ b/ironfish-cli/src/commands/wallet/transaction/view.ts @@ -39,7 +39,7 @@ export class TransactionViewCommand extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(TransactionViewCommand) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const account = flags.account ?? (await this.selectAccount(client)) diff --git a/ironfish-cli/src/commands/wallet/transaction/watch.ts b/ironfish-cli/src/commands/wallet/transaction/watch.ts index f8d82600dc..46b80853d2 100644 --- a/ironfish-cli/src/commands/wallet/transaction/watch.ts +++ b/ironfish-cli/src/commands/wallet/transaction/watch.ts @@ -38,7 +38,7 @@ export class WatchTxCommand extends IronfishCommand { // TODO: remove account arg const account = flags.account ? flags.account : args.account - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() await watchTransaction({ client, diff --git a/ironfish-cli/src/commands/wallet/transactions.ts b/ironfish-cli/src/commands/wallet/transactions.ts index 0c09e2e6a3..49cff44e0d 100644 --- a/ironfish-cli/src/commands/wallet/transactions.ts +++ b/ironfish-cli/src/commands/wallet/transactions.ts @@ -71,7 +71,7 @@ export class TransactionsCommand extends IronfishCommand { ? Format.json : Format.cli - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const networkId = (await client.chain.getNetworkInfo()).content.networkId diff --git a/ironfish-cli/src/commands/wallet/use.ts b/ironfish-cli/src/commands/wallet/use.ts index ffda39750a..165f8d6c60 100644 --- a/ironfish-cli/src/commands/wallet/use.ts +++ b/ironfish-cli/src/commands/wallet/use.ts @@ -23,7 +23,7 @@ export class UseCommand extends IronfishCommand { const { args } = await this.parse(UseCommand) const { account } = args - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() await client.wallet.useAccount({ account }) this.log(`The default account is now: ${account}`) } diff --git a/ironfish-cli/src/commands/wallet/which.ts b/ironfish-cli/src/commands/wallet/which.ts index 404386120c..3317328aec 100644 --- a/ironfish-cli/src/commands/wallet/which.ts +++ b/ironfish-cli/src/commands/wallet/which.ts @@ -23,7 +23,7 @@ export class WhichCommand extends IronfishCommand { async start(): Promise { const { flags } = await this.parse(WhichCommand) - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const { content: { diff --git a/ironfish-cli/src/commands/workers/status.ts b/ironfish-cli/src/commands/workers/status.ts index 58ac797133..69446c663a 100644 --- a/ironfish-cli/src/commands/workers/status.ts +++ b/ironfish-cli/src/commands/workers/status.ts @@ -23,7 +23,7 @@ export default class Status extends IronfishCommand { const { flags } = await this.parse(Status) if (!flags.follow) { - const client = await this.sdk.connectRpc() + const client = await this.connectRpc() const response = await client.worker.getWorkersStatus() this.log(renderStatus(response.content)) this.exit(0) diff --git a/ironfish/src/rpc/clients/client.ts b/ironfish/src/rpc/clients/client.ts index cb179e9bfe..f8b70ac435 100644 --- a/ironfish/src/rpc/clients/client.ts +++ b/ironfish/src/rpc/clients/client.ts @@ -176,6 +176,8 @@ import type { import { ApiNamespace } from '../routes/namespaces' export abstract class RpcClient { + abstract close(): void + abstract request( route: string, data?: unknown, diff --git a/ironfish/src/rpc/clients/memoryClient.ts b/ironfish/src/rpc/clients/memoryClient.ts index 436f3c4fe4..69998ce565 100644 --- a/ironfish/src/rpc/clients/memoryClient.ts +++ b/ironfish/src/rpc/clients/memoryClient.ts @@ -29,4 +29,6 @@ export class RpcMemoryClient extends RpcClient { return RpcMemoryAdapter.requestStream(this.router, route, data) } + + close(): void {} } From 44e2c7fcb07fc7171adbd2bf4ceb8fcdea5e2d3a Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Wed, 24 Jul 2024 13:25:10 -0700 Subject: [PATCH 71/81] Convert alises to hiddenAlias (#5178) This should make them not discoverable, but users can use them still. --- ironfish-cli/src/commands/wallet/transaction/import.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironfish-cli/src/commands/wallet/transaction/import.ts b/ironfish-cli/src/commands/wallet/transaction/import.ts index e556007ca3..e9b1232c93 100644 --- a/ironfish-cli/src/commands/wallet/transaction/import.ts +++ b/ironfish-cli/src/commands/wallet/transaction/import.ts @@ -9,7 +9,7 @@ import { importFile, importPipe, longPrompt } from '../../../utils/input' export class TransactionImportCommand extends IronfishCommand { static description = `Import a transaction into your wallet` - static aliases = ['wallet:transaction:add'] + static hiddenAliases = ['wallet:transaction:add'] static flags = { ...RemoteFlags, From 105a03e299a4f1b79fad60be62ec45a0c99d1ea1 Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Thu, 25 Jul 2024 09:09:48 -0700 Subject: [PATCH 72/81] Add Config, DataDir and Verbose flags as base flags to all commands (#5174) This removes LocalFlags entirely, as well as removing these 3 flags from RemoteFlags. --- ironfish-cli/src/command.ts | 8 ++++++++ ironfish-cli/src/commands/browse.ts | 3 --- ironfish-cli/src/commands/chain/benchmark.ts | 2 -- ironfish-cli/src/commands/chain/blocks/info.ts | 5 ----- ironfish-cli/src/commands/chain/download.ts | 2 -- ironfish-cli/src/commands/chain/genesisadd.ts | 2 -- ironfish-cli/src/commands/chain/genesisblock.ts | 2 -- ironfish-cli/src/commands/chain/power.ts | 2 -- ironfish-cli/src/commands/chain/prune.ts | 2 -- ironfish-cli/src/commands/chain/repair.ts | 2 -- ironfish-cli/src/commands/chain/rewind.ts | 2 -- ironfish-cli/src/commands/chain/status.ts | 5 ----- ironfish-cli/src/commands/config/edit.ts | 3 --- ironfish-cli/src/commands/debug.ts | 5 ----- ironfish-cli/src/commands/migrations/index.ts | 6 ------ ironfish-cli/src/commands/migrations/revert.ts | 6 ------ ironfish-cli/src/commands/migrations/start.ts | 4 ---- ironfish-cli/src/commands/repl.ts | 11 ----------- ironfish-cli/src/commands/reset.ts | 11 ----------- ironfish-cli/src/commands/rpc/token.ts | 2 -- ironfish-cli/src/commands/start.ts | 9 --------- ironfish-cli/src/commands/wallet/prune.ts | 2 -- ironfish-cli/src/flags.ts | 13 ------------- 23 files changed, 8 insertions(+), 101 deletions(-) diff --git a/ironfish-cli/src/command.ts b/ironfish-cli/src/command.ts index 67086d6c0e..164ec5e2f8 100644 --- a/ironfish-cli/src/command.ts +++ b/ironfish-cli/src/command.ts @@ -15,7 +15,9 @@ import { import { Command, Config, ux } from '@oclif/core' import { CLIError, ExitError } from '@oclif/core/errors' import { + ConfigFlag, ConfigFlagKey, + DataDirFlag, DataDirFlagKey, RpcAuthFlagKey, RpcHttpHostFlagKey, @@ -72,6 +74,12 @@ export abstract class IronfishCommand extends Command { client: RpcClient | null = null + public static baseFlags = { + [VerboseFlagKey]: VerboseFlag, + [ConfigFlagKey]: ConfigFlag, + [DataDirFlagKey]: DataDirFlag, + } + constructor(argv: string[], config: Config) { super(argv, config) this.logger = createRootLogger().withTag(this.ctor.id) diff --git a/ironfish-cli/src/commands/browse.ts b/ironfish-cli/src/commands/browse.ts index 6270b6c54a..5faa6bcbad 100644 --- a/ironfish-cli/src/commands/browse.ts +++ b/ironfish-cli/src/commands/browse.ts @@ -4,15 +4,12 @@ import { Platform } from '@ironfish/sdk' import { Flags } from '@oclif/core' import { IronfishCommand } from '../command' -import { ConfigFlag, ConfigFlagKey, DataDirFlag, DataDirFlagKey } from '../flags' import { PlatformUtils } from '../utils' export class BrowseCommand extends IronfishCommand { static description = `Browse to your data directory` static flags = { - [ConfigFlagKey]: ConfigFlag, - [DataDirFlagKey]: DataDirFlag, cd: Flags.boolean({ default: false, description: 'print the directory where the data directory is', diff --git a/ironfish-cli/src/commands/chain/benchmark.ts b/ironfish-cli/src/commands/chain/benchmark.ts index a00bcf9452..46848f8ffc 100644 --- a/ironfish-cli/src/commands/chain/benchmark.ts +++ b/ironfish-cli/src/commands/chain/benchmark.ts @@ -7,7 +7,6 @@ import blessed from 'blessed' import fs from 'fs/promises' import path from 'path' import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' import { IronfishCliPKG } from '../../package' import * as ui from '../../ui' @@ -18,7 +17,6 @@ export default class Benchmark extends IronfishCommand { static hidden = true static flags = { - ...LocalFlags, targetdir: Flags.string({ char: 't', required: false, diff --git a/ironfish-cli/src/commands/chain/blocks/info.ts b/ironfish-cli/src/commands/chain/blocks/info.ts index ca30eed467..f40e617e4b 100644 --- a/ironfish-cli/src/commands/chain/blocks/info.ts +++ b/ironfish-cli/src/commands/chain/blocks/info.ts @@ -4,7 +4,6 @@ import { BufferUtils, TimeUtils } from '@ironfish/sdk' import { Args } from '@oclif/core' import { IronfishCommand } from '../../../command' -import { LocalFlags } from '../../../flags' import * as ui from '../../../ui' export default class BlockInfo extends IronfishCommand { @@ -17,10 +16,6 @@ export default class BlockInfo extends IronfishCommand { }), } - static flags = { - ...LocalFlags, - } - static enableJsonFlag: boolean = true async start(): Promise { diff --git a/ironfish-cli/src/commands/chain/download.ts b/ironfish-cli/src/commands/chain/download.ts index cad37a66f1..1ae08d936d 100644 --- a/ironfish-cli/src/commands/chain/download.ts +++ b/ironfish-cli/src/commands/chain/download.ts @@ -5,7 +5,6 @@ import { ErrorUtils, FileUtils, NodeUtils } from '@ironfish/sdk' import { Flags, ux } from '@oclif/core' import fsAsync from 'fs/promises' import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' import { DownloadedSnapshot, getDefaultManifestUrl, SnapshotDownloader } from '../../snapshot' import { confirmOrQuit, ProgressBar, ProgressBarPresets } from '../../ui' @@ -13,7 +12,6 @@ export default class Download extends IronfishCommand { static description = 'download the chain' static flags = { - ...LocalFlags, manifestUrl: Flags.string({ char: 'm', description: 'Manifest url to download snapshot from', diff --git a/ironfish-cli/src/commands/chain/genesisadd.ts b/ironfish-cli/src/commands/chain/genesisadd.ts index 947376fba4..12767c0053 100644 --- a/ironfish-cli/src/commands/chain/genesisadd.ts +++ b/ironfish-cli/src/commands/chain/genesisadd.ts @@ -13,14 +13,12 @@ import { import { Flags } from '@oclif/core' import fs from 'fs/promises' import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' import { confirmOrQuit, table, TableColumns } from '../../ui' export default class GenesisAddCommand extends IronfishCommand { static hidden = true static flags = { - ...LocalFlags, account: Flags.string({ char: 'a', required: true, diff --git a/ironfish-cli/src/commands/chain/genesisblock.ts b/ironfish-cli/src/commands/chain/genesisblock.ts index 9365e4ba15..f350a2a36f 100644 --- a/ironfish-cli/src/commands/chain/genesisblock.ts +++ b/ironfish-cli/src/commands/chain/genesisblock.ts @@ -15,7 +15,6 @@ import { import { Flags } from '@oclif/core' import fs from 'fs/promises' import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' import { confirmOrQuit, table, TableColumns } from '../../ui' export default class GenesisBlockCommand extends IronfishCommand { @@ -24,7 +23,6 @@ export default class GenesisBlockCommand extends IronfishCommand { static hidden = true static flags = { - ...LocalFlags, account: Flags.string({ char: 'a', required: false, diff --git a/ironfish-cli/src/commands/chain/power.ts b/ironfish-cli/src/commands/chain/power.ts index b551ac0cbf..e3df7e5a0d 100644 --- a/ironfish-cli/src/commands/chain/power.ts +++ b/ironfish-cli/src/commands/chain/power.ts @@ -4,13 +4,11 @@ import { FileUtils } from '@ironfish/sdk' import { Args, Flags } from '@oclif/core' import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' export default class Power extends IronfishCommand { static description = "show the network's mining power" static flags = { - ...LocalFlags, history: Flags.integer({ required: false, description: diff --git a/ironfish-cli/src/commands/chain/prune.ts b/ironfish-cli/src/commands/chain/prune.ts index ff1b493740..19527e45d6 100644 --- a/ironfish-cli/src/commands/chain/prune.ts +++ b/ironfish-cli/src/commands/chain/prune.ts @@ -4,13 +4,11 @@ import { BlockchainUtils } from '@ironfish/sdk' import { Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' export default class Prune extends IronfishCommand { static description = 'remove unused blocks from the chain' static flags = { - ...LocalFlags, dry: Flags.boolean({ default: false, description: 'Dry run prune first', diff --git a/ironfish-cli/src/commands/chain/repair.ts b/ironfish-cli/src/commands/chain/repair.ts index 516112ddf6..29c5fe6f61 100644 --- a/ironfish-cli/src/commands/chain/repair.ts +++ b/ironfish-cli/src/commands/chain/repair.ts @@ -4,7 +4,6 @@ import { Assert, BlockHeader, FullNode, IDatabaseTransaction, TimeUtils } from '@ironfish/sdk' import { Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' import { confirmOrQuit, ProgressBar, ProgressBarPresets } from '../../ui' const TREE_BATCH = 1000 @@ -20,7 +19,6 @@ export default class RepairChain extends IronfishCommand { static hidden = true static flags = { - ...LocalFlags, confirm: Flags.boolean({ char: 'c', default: false, diff --git a/ironfish-cli/src/commands/chain/rewind.ts b/ironfish-cli/src/commands/chain/rewind.ts index c7bb2ebe7a..68cbc557d0 100644 --- a/ironfish-cli/src/commands/chain/rewind.ts +++ b/ironfish-cli/src/commands/chain/rewind.ts @@ -4,7 +4,6 @@ import { Assert, Blockchain, BlockchainUtils, FullNode, NodeUtils, Wallet } from '@ironfish/sdk' import { Args, Command, Flags } from '@oclif/core' import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' import { ProgressBar, ProgressBarPresets } from '../../ui' export default class Rewind extends IronfishCommand { @@ -24,7 +23,6 @@ export default class Rewind extends IronfishCommand { } static flags = { - ...LocalFlags, wallet: Flags.boolean({ default: true, allowNo: true, diff --git a/ironfish-cli/src/commands/chain/status.ts b/ironfish-cli/src/commands/chain/status.ts index a560f23b04..2c2efd9eda 100644 --- a/ironfish-cli/src/commands/chain/status.ts +++ b/ironfish-cli/src/commands/chain/status.ts @@ -3,16 +3,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { FileUtils, renderNetworkName } from '@ironfish/sdk' import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' import * as ui from '../../ui' export default class ChainStatus extends IronfishCommand { static description = 'show chain information' - static flags = { - ...LocalFlags, - } - async start(): Promise { const client = await this.connectRpc() diff --git a/ironfish-cli/src/commands/config/edit.ts b/ironfish-cli/src/commands/config/edit.ts index 5b951f6c93..480cc7249e 100644 --- a/ironfish-cli/src/commands/config/edit.ts +++ b/ironfish-cli/src/commands/config/edit.ts @@ -8,7 +8,6 @@ import os from 'os' import path from 'path' import { promisify } from 'util' import { IronfishCommand } from '../../command' -import { ConfigFlag, ConfigFlagKey, DataDirFlag, DataDirFlagKey } from '../../flags' import { launchEditor } from '../../utils' const mkdtempAsync = promisify(mkdtemp) @@ -21,8 +20,6 @@ export class EditCommand extends IronfishCommand { Set the editor in either EDITOR environment variable, or set 'editor' in your ironfish config` static flags = { - [ConfigFlagKey]: ConfigFlag, - [DataDirFlagKey]: DataDirFlag, remote: Flags.boolean({ default: false, description: 'Connect to the node when editing the config', diff --git a/ironfish-cli/src/commands/debug.ts b/ironfish-cli/src/commands/debug.ts index b9b8827ea7..d0d4feeffa 100644 --- a/ironfish-cli/src/commands/debug.ts +++ b/ironfish-cli/src/commands/debug.ts @@ -13,7 +13,6 @@ import { execSync } from 'child_process' import os from 'os' import { getHeapStatistics } from 'v8' import { IronfishCommand } from '../command' -import { LocalFlags } from '../flags' const SPACE_BUFFER = 8 @@ -21,10 +20,6 @@ export default class Debug extends IronfishCommand { static description = 'Show debug information to help locate issues' static hidden = true - static flags = { - ...LocalFlags, - } - async start(): Promise { const node = await this.sdk.node({ autoSeed: false }) diff --git a/ironfish-cli/src/commands/migrations/index.ts b/ironfish-cli/src/commands/migrations/index.ts index 0845878cf4..8a5b047451 100644 --- a/ironfish-cli/src/commands/migrations/index.ts +++ b/ironfish-cli/src/commands/migrations/index.ts @@ -2,16 +2,10 @@ * 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 { IronfishCommand } from '../../command' -import { ConfigFlag, ConfigFlagKey, DataDirFlag, DataDirFlagKey } from '../../flags' export class StatusCommand extends IronfishCommand { static description = `List all the migration statuses` - static flags = { - [ConfigFlagKey]: ConfigFlag, - [DataDirFlagKey]: DataDirFlag, - } - async start(): Promise { await this.parse(StatusCommand) diff --git a/ironfish-cli/src/commands/migrations/revert.ts b/ironfish-cli/src/commands/migrations/revert.ts index dd0168197d..ba4e1a17f6 100644 --- a/ironfish-cli/src/commands/migrations/revert.ts +++ b/ironfish-cli/src/commands/migrations/revert.ts @@ -2,18 +2,12 @@ * 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 { IronfishCommand } from '../../command' -import { ConfigFlag, ConfigFlagKey, DataDirFlag, DataDirFlagKey } from '../../flags' export class RevertCommand extends IronfishCommand { static description = `Revert the last run migration` static hidden = true - static flags = { - [ConfigFlagKey]: ConfigFlag, - [DataDirFlagKey]: DataDirFlag, - } - async start(): Promise { await this.parse(RevertCommand) diff --git a/ironfish-cli/src/commands/migrations/start.ts b/ironfish-cli/src/commands/migrations/start.ts index 2ac49734ba..099fc38835 100644 --- a/ironfish-cli/src/commands/migrations/start.ts +++ b/ironfish-cli/src/commands/migrations/start.ts @@ -3,15 +3,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { Flags } from '@oclif/core' import { IronfishCommand } from '../../command' -import { ConfigFlag, ConfigFlagKey, DataDirFlag, DataDirFlagKey, LocalFlags } from '../../flags' export class StartCommand extends IronfishCommand { static description = `Run migrations` static flags = { - ...LocalFlags, - [ConfigFlagKey]: ConfigFlag, - [DataDirFlagKey]: DataDirFlag, dry: Flags.boolean({ default: false, description: 'Dry run migrations first', diff --git a/ironfish-cli/src/commands/repl.ts b/ironfish-cli/src/commands/repl.ts index 62c06fa8c1..7a7d96c2e3 100644 --- a/ironfish-cli/src/commands/repl.ts +++ b/ironfish-cli/src/commands/repl.ts @@ -8,22 +8,11 @@ import fs from 'fs/promises' import repl from 'node:repl' import path from 'path' import { IronfishCommand } from '../command' -import { - ConfigFlag, - ConfigFlagKey, - DataDirFlag, - DataDirFlagKey, - VerboseFlag, - VerboseFlagKey, -} from '../flags' export default class Repl extends IronfishCommand { static description = 'An interactive terminal to the node' static flags = { - [VerboseFlagKey]: VerboseFlag, - [ConfigFlagKey]: ConfigFlag, - [DataDirFlagKey]: DataDirFlag, opendb: Flags.boolean({ description: 'open the databases', allowNo: true, diff --git a/ironfish-cli/src/commands/reset.ts b/ironfish-cli/src/commands/reset.ts index 34b441ff8e..ddd87f37a2 100644 --- a/ironfish-cli/src/commands/reset.ts +++ b/ironfish-cli/src/commands/reset.ts @@ -5,23 +5,12 @@ import { FullNode, PEER_STORE_FILE_NAME } from '@ironfish/sdk' import { Flags, ux } from '@oclif/core' import fsAsync from 'fs/promises' import { IronfishCommand } from '../command' -import { - ConfigFlag, - ConfigFlagKey, - DataDirFlag, - DataDirFlagKey, - VerboseFlag, - VerboseFlagKey, -} from '../flags' import { confirmOrQuit } from '../ui' export default class Reset extends IronfishCommand { static description = 'Reset the node to its initial state' static flags = { - [VerboseFlagKey]: VerboseFlag, - [ConfigFlagKey]: ConfigFlag, - [DataDirFlagKey]: DataDirFlag, networkId: Flags.integer({ char: 'i', default: undefined, diff --git a/ironfish-cli/src/commands/rpc/token.ts b/ironfish-cli/src/commands/rpc/token.ts index 34a3feeeda..5ca6dbf314 100644 --- a/ironfish-cli/src/commands/rpc/token.ts +++ b/ironfish-cli/src/commands/rpc/token.ts @@ -3,13 +3,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { Flags } from '@oclif/core' import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' export default class Token extends IronfishCommand { static description = 'Get or set the RPC auth token' static flags = { - ...LocalFlags, token: Flags.string({ required: false, description: 'Set the RPC auth token to ', diff --git a/ironfish-cli/src/commands/start.ts b/ironfish-cli/src/commands/start.ts index 44805b3ec9..09231246f8 100644 --- a/ironfish-cli/src/commands/start.ts +++ b/ironfish-cli/src/commands/start.ts @@ -7,10 +7,6 @@ import inspector from 'node:inspector' import { v4 as uuid } from 'uuid' import { IronfishCommand, SIGNALS } from '../command' import { - ConfigFlag, - ConfigFlagKey, - DataDirFlag, - DataDirFlagKey, RpcAuthFlag, RpcAuthFlagKey, RpcHttpHostFlag, @@ -29,8 +25,6 @@ import { RpcUseIpcFlagKey, RpcUseTcpFlag, RpcUseTcpFlagKey, - VerboseFlag, - VerboseFlagKey, } from '../flags' import { ONE_FISH_IMAGE } from '../images' @@ -41,9 +35,6 @@ export default class Start extends IronfishCommand { static description = 'Start the node' static flags = { - [VerboseFlagKey]: VerboseFlag, - [ConfigFlagKey]: ConfigFlag, - [DataDirFlagKey]: DataDirFlag, [RpcUseIpcFlagKey]: { ...RpcUseIpcFlag, allowNo: true } as typeof RpcUseIpcFlag, [RpcUseTcpFlagKey]: { ...RpcUseTcpFlag, allowNo: true } as typeof RpcUseTcpFlag, [RpcUseHttpFlagKey]: { ...RpcUseHttpFlag, allowNo: true } as typeof RpcUseHttpFlag, diff --git a/ironfish-cli/src/commands/wallet/prune.ts b/ironfish-cli/src/commands/wallet/prune.ts index 2deb840324..9347b935a1 100644 --- a/ironfish-cli/src/commands/wallet/prune.ts +++ b/ironfish-cli/src/commands/wallet/prune.ts @@ -4,13 +4,11 @@ import { NodeUtils, TransactionStatus } from '@ironfish/sdk' import { Flags, ux } from '@oclif/core' import { IronfishCommand } from '../../command' -import { LocalFlags } from '../../flags' export default class PruneCommand extends IronfishCommand { static description = 'Removes expired transactions from the wallet' static flags = { - ...LocalFlags, dryrun: Flags.boolean({ default: false, description: 'Dry run prune first', diff --git a/ironfish-cli/src/flags.ts b/ironfish-cli/src/flags.ts index b4920b456c..a3251055a5 100644 --- a/ironfish-cli/src/flags.ts +++ b/ironfish-cli/src/flags.ts @@ -96,24 +96,11 @@ export const RpcUseHttpFlag = Flags.boolean({ allowNo: true, }) -/** - * These flags should usually be used on any command that starts a node, - * or uses a database to execute the command - */ -export const LocalFlags = { - [VerboseFlagKey]: VerboseFlag, - [ConfigFlagKey]: ConfigFlag, - [DataDirFlagKey]: DataDirFlag, -} - /** * These flags should usually be used on any command that uses an * RPC client to connect to a node to run the command */ export const RemoteFlags = { - [VerboseFlagKey]: VerboseFlag, - [ConfigFlagKey]: ConfigFlag, - [DataDirFlagKey]: DataDirFlag, [RpcUseTcpFlagKey]: RpcUseTcpFlag, [RpcUseIpcFlagKey]: RpcUseIpcFlag, [RpcTcpHostFlagKey]: RpcTcpHostFlag, From 88f16bccbf15e109f28ce501485ad3d355ab355a Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Fri, 26 Jul 2024 14:18:31 -0700 Subject: [PATCH 73/81] Move the flags in baseFlags into the global category (#5180) --- ironfish-cli/src/flags.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ironfish-cli/src/flags.ts b/ironfish-cli/src/flags.ts index a3251055a5..f8ee624c88 100644 --- a/ironfish-cli/src/flags.ts +++ b/ironfish-cli/src/flags.ts @@ -34,6 +34,7 @@ export const VerboseFlag = Flags.boolean({ char: 'v', default: false, description: 'Set logging level to verbose', + helpGroup: 'GLOBAL', }) export const ColorFlag = Flags.boolean({ @@ -45,6 +46,7 @@ export const ColorFlag = Flags.boolean({ export const ConfigFlag = Flags.string({ default: DEFAULT_CONFIG_NAME, description: 'The name of the config file to use', + helpGroup: 'GLOBAL', }) export const DataDirFlag = Flags.string({ @@ -52,6 +54,7 @@ export const DataDirFlag = Flags.string({ default: DEFAULT_DATA_DIR, description: 'The path to the data dir', env: 'IRONFISH_DATA_DIR', + helpGroup: 'GLOBAL', }) export const RpcUseIpcFlag = Flags.boolean({ From 0eaa25ff750a145b0047ce73dcf1b21cd0bc4235 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Mon, 29 Jul 2024 15:59:12 -0700 Subject: [PATCH 74/81] Add mining reward to chain:blocks:info (#5185) --- ironfish-cli/src/commands/chain/blocks/info.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ironfish-cli/src/commands/chain/blocks/info.ts b/ironfish-cli/src/commands/chain/blocks/info.ts index f40e617e4b..2f8169c072 100644 --- a/ironfish-cli/src/commands/chain/blocks/info.ts +++ b/ironfish-cli/src/commands/chain/blocks/info.ts @@ -1,7 +1,7 @@ /* 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 { BufferUtils, TimeUtils } from '@ironfish/sdk' +import { BufferUtils, CurrencyUtils, TimeUtils } from '@ironfish/sdk' import { Args } from '@oclif/core' import { IronfishCommand } from '../../../command' import * as ui from '../../../ui' @@ -26,6 +26,8 @@ export default class BlockInfo extends IronfishCommand { const data = await client.chain.getBlock({ search }) const blockData = data.content + const miningReward = blockData.block.transactions[0] + this.log( ui.card({ Hash: blockData.block.hash, @@ -36,6 +38,7 @@ export default class BlockInfo extends IronfishCommand { Difficulty: blockData.block.difficulty, Timestamp: TimeUtils.renderString(blockData.block.timestamp), Graffiti: BufferUtils.toHuman(Buffer.from(blockData.block.graffiti, 'hex')), + 'Mining Reward': CurrencyUtils.render((miningReward.fee * -1).toString(), true), 'Transaction Count': blockData.block.transactions.length, }), ) From 3c457074884cc3ffe71be2118b9ada388c8f64ea Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:58:12 -0700 Subject: [PATCH 75/81] CLI commands: use built-in JSON functionality instead of re-implementing it as needed (#5175) --- .../src/commands/chain/blocks/info.ts | 3 +- ironfish-cli/src/commands/config/get.ts | 33 +++++-------------- ironfish-cli/src/commands/config/index.ts | 14 ++++---- ironfish-cli/src/commands/wallet/export.ts | 19 +++++------ ironfish-cli/src/ui/json.ts | 9 ++++- 5 files changed, 32 insertions(+), 46 deletions(-) diff --git a/ironfish-cli/src/commands/chain/blocks/info.ts b/ironfish-cli/src/commands/chain/blocks/info.ts index 2f8169c072..7116d5a8af 100644 --- a/ironfish-cli/src/commands/chain/blocks/info.ts +++ b/ironfish-cli/src/commands/chain/blocks/info.ts @@ -8,6 +8,7 @@ import * as ui from '../../../ui' export default class BlockInfo extends IronfishCommand { static description = 'Show the block header of a requested hash or sequence' + static enableJsonFlag = true static args = { search: Args.string({ @@ -16,8 +17,6 @@ export default class BlockInfo extends IronfishCommand { }), } - static enableJsonFlag: boolean = true - async start(): Promise { const { args } = await this.parse(BlockInfo) const { search } = args diff --git a/ironfish-cli/src/commands/config/get.ts b/ironfish-cli/src/commands/config/get.ts index 47f83275ad..0f5a91c1a3 100644 --- a/ironfish-cli/src/commands/config/get.ts +++ b/ironfish-cli/src/commands/config/get.ts @@ -3,12 +3,13 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { ConfigOptions } from '@ironfish/sdk' import { Args, Flags } from '@oclif/core' -import jsonColorizer from 'json-colorizer' import { IronfishCommand } from '../../command' -import { RemoteFlags } from '../../flags' +import { ColorFlag, ColorFlagKey, RemoteFlags } from '../../flags' +import * as ui from '../../ui' export class GetCommand extends IronfishCommand { static description = `Print out one config value` + static enableJsonFlag = true static args = { name: Args.string({ @@ -19,6 +20,7 @@ export class GetCommand extends IronfishCommand { static flags = { ...RemoteFlags, + [ColorFlagKey]: ColorFlag, user: Flags.boolean({ description: 'Only show config from the users datadir and not overrides', }), @@ -26,19 +28,9 @@ export class GetCommand extends IronfishCommand { default: false, description: 'Dont connect to the node when displaying the config', }), - color: Flags.boolean({ - default: true, - allowNo: true, - description: 'Should colorize the output', - }), - json: Flags.boolean({ - default: false, - allowNo: true, - description: 'Output the config value as json', - }), } - async start(): Promise { + async start(): Promise { const { args, flags } = await this.parse(GetCommand) const { name } = args @@ -54,19 +46,10 @@ export class GetCommand extends IronfishCommand { this.exit(0) } - let output = '' + const config = { [key]: response.content[key] } - if (flags.json) { - output = JSON.stringify(response.content[key], undefined, ' ') - - if (flags.color) { - output = jsonColorizer(output) - } - } else { - output = String(response.content[key]) - } + this.log(ui.card(config)) - this.log(output) - this.exit(0) + return config } } diff --git a/ironfish-cli/src/commands/config/index.ts b/ironfish-cli/src/commands/config/index.ts index 49995959f1..5f4a18142e 100644 --- a/ironfish-cli/src/commands/config/index.ts +++ b/ironfish-cli/src/commands/config/index.ts @@ -2,13 +2,14 @@ * 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 { Flags } from '@oclif/core' -import jsonColorizer from 'json-colorizer' import { IronfishCommand } from '../../command' import { ColorFlag, ColorFlagKey } from '../../flags' import { RemoteFlags } from '../../flags' +import * as ui from '../../ui' export class ShowCommand extends IronfishCommand { static description = `Print out the entire config` + static enableJsonFlag = true static flags = { ...RemoteFlags, @@ -22,16 +23,15 @@ export class ShowCommand extends IronfishCommand { }), } - async start(): Promise { + async start(): Promise { const { flags } = await this.parse(ShowCommand) const client = await this.connectRpc(flags.local) const response = await client.config.getConfig({ user: flags.user }) + const config = response.content - let output = JSON.stringify(response.content, undefined, ' ') - if (flags.color) { - output = jsonColorizer(output) - } - this.log(output) + this.log(ui.card(config)) + + return config } } diff --git a/ironfish-cli/src/commands/wallet/export.ts b/ironfish-cli/src/commands/wallet/export.ts index 3a271fef62..835e9cbacf 100644 --- a/ironfish-cli/src/commands/wallet/export.ts +++ b/ironfish-cli/src/commands/wallet/export.ts @@ -4,7 +4,6 @@ import { AccountFormat, ErrorUtils, LanguageUtils } from '@ironfish/sdk' import { Args, Flags } from '@oclif/core' import fs from 'fs' -import jsonColorizer from 'json-colorizer' import path from 'path' import { IronfishCommand } from '../../command' import { ColorFlag, ColorFlagKey, EnumLanguageKeyFlag, RemoteFlags } from '../../flags' @@ -12,6 +11,7 @@ import { confirmOrQuit } from '../../ui' export class ExportCommand extends IronfishCommand { static description = `Export an account` + static enableJsonFlag = true static flags = { ...RemoteFlags, @@ -29,10 +29,6 @@ export class ExportCommand extends IronfishCommand { required: false, choices: LanguageUtils.LANGUAGE_KEYS, }), - json: Flags.boolean({ - default: false, - description: 'Output the account as JSON, rather than the default bech32', - }), path: Flags.string({ description: 'The path to export the account to', required: false, @@ -50,9 +46,9 @@ export class ExportCommand extends IronfishCommand { }), } - async start(): Promise { + async start(): Promise { const { flags, args } = await this.parse(ExportCommand) - const { color, local, path: exportPath, viewonly: viewOnly } = flags + const { local, path: exportPath, viewonly: viewOnly } = flags const { account } = args if (flags.language) { @@ -73,10 +69,7 @@ export class ExportCommand extends IronfishCommand { language: flags.language, }) - let output = response.content.account - if (color && flags.json && !exportPath) { - output = jsonColorizer(output) - } + const output = response.content.account if (exportPath) { let resolved = this.sdk.fileSystem.resolve(exportPath) @@ -110,5 +103,9 @@ export class ExportCommand extends IronfishCommand { } this.log(output) + + if (flags.json) { + return output + } } } diff --git a/ironfish-cli/src/ui/json.ts b/ironfish-cli/src/ui/json.ts index bcd1196370..70b0838e28 100644 --- a/ironfish-cli/src/ui/json.ts +++ b/ironfish-cli/src/ui/json.ts @@ -5,5 +5,12 @@ import jsonColorizer from 'json-colorizer' export function json(data: unknown): string { - return jsonColorizer(JSON.stringify(data, undefined, ' ')) + let output = data + + // Only try to stringify JSON output if it is not already a string + if (typeof data !== 'string') { + output = JSON.stringify(data, undefined, ' ') + } + + return jsonColorizer(output) } From a641103c0f1e6e15b414aa187e16bc9c68ccdcf0 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Mon, 29 Jul 2024 17:26:18 -0700 Subject: [PATCH 76/81] Have chain:power support --json flag (#5187) This makes chain:power standard and return JSON when you pass in --json flag and adds the --color flag support as well. --- ironfish-cli/src/commands/chain/power.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ironfish-cli/src/commands/chain/power.ts b/ironfish-cli/src/commands/chain/power.ts index e3df7e5a0d..632448e77d 100644 --- a/ironfish-cli/src/commands/chain/power.ts +++ b/ironfish-cli/src/commands/chain/power.ts @@ -4,11 +4,14 @@ import { FileUtils } from '@ironfish/sdk' import { Args, Flags } from '@oclif/core' import { IronfishCommand } from '../../command' +import { ColorFlag, ColorFlagKey } from '../../flags' export default class Power extends IronfishCommand { static description = "show the network's mining power" + static enableJsonFlag = true static flags = { + [ColorFlagKey]: ColorFlag, history: Flags.integer({ required: false, description: @@ -23,14 +26,13 @@ export default class Power extends IronfishCommand { }), } - async start(): Promise { + async start(): Promise { const { flags, args } = await this.parse(Power) - const { block } = args const client = await this.connectRpc() const data = await client.chain.getNetworkHashPower({ - sequence: block, + sequence: args.block, blocks: flags.history, }) @@ -39,5 +41,7 @@ export default class Power extends IronfishCommand { this.log( `The network power for block ${data.content.sequence} was ${formattedHashesPerSecond} averaged over ${data.content.blocks} previous blocks.`, ) + + return data.content } } From d154e24db4afb9b3d73345f85be16c69d8255f9f Mon Sep 17 00:00:00 2001 From: mat-if <97762857+mat-if@users.noreply.github.com> Date: Mon, 29 Jul 2024 17:26:49 -0700 Subject: [PATCH 77/81] Add json support for chain:assets:info (#5186) --- ironfish-cli/src/commands/chain/assets/info.ts | 8 ++++++-- ironfish-cli/src/commands/chain/blocks/info.ts | 5 +++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ironfish-cli/src/commands/chain/assets/info.ts b/ironfish-cli/src/commands/chain/assets/info.ts index d30dccf669..354e9e40a7 100644 --- a/ironfish-cli/src/commands/chain/assets/info.ts +++ b/ironfish-cli/src/commands/chain/assets/info.ts @@ -4,11 +4,12 @@ import { BufferUtils } from '@ironfish/sdk' import { Args } from '@oclif/core' import { IronfishCommand } from '../../../command' -import { RemoteFlags } from '../../../flags' +import { ColorFlag, ColorFlagKey, RemoteFlags } from '../../../flags' import * as ui from '../../../ui' export default class AssetInfo extends IronfishCommand { static description = 'show asset information' + static enableJsonFlag = true static args = { id: Args.string({ @@ -19,9 +20,10 @@ export default class AssetInfo extends IronfishCommand { static flags = { ...RemoteFlags, + [ColorFlagKey]: ColorFlag, } - async start(): Promise { + async start(): Promise { const { args } = await this.parse(AssetInfo) const { id: assetId } = args @@ -39,5 +41,7 @@ export default class AssetInfo extends IronfishCommand { 'Transaction Created': data.content.createdTransactionHash, }), ) + + return data.content } } diff --git a/ironfish-cli/src/commands/chain/blocks/info.ts b/ironfish-cli/src/commands/chain/blocks/info.ts index 7116d5a8af..b3bdfecc7e 100644 --- a/ironfish-cli/src/commands/chain/blocks/info.ts +++ b/ironfish-cli/src/commands/chain/blocks/info.ts @@ -4,6 +4,7 @@ import { BufferUtils, CurrencyUtils, TimeUtils } from '@ironfish/sdk' import { Args } from '@oclif/core' import { IronfishCommand } from '../../../command' +import { ColorFlag, ColorFlagKey } from '../../../flags' import * as ui from '../../../ui' export default class BlockInfo extends IronfishCommand { @@ -17,6 +18,10 @@ export default class BlockInfo extends IronfishCommand { }), } + static flags = { + [ColorFlagKey]: ColorFlag, + } + async start(): Promise { const { args } = await this.parse(BlockInfo) const { search } = args From d406695a070d185cd3a7c6dbee7ff06a0e1d1050 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Mon, 29 Jul 2024 18:05:16 -0700 Subject: [PATCH 78/81] Add --json support for chain:status (#5188) --- ironfish-cli/src/commands/chain/status.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/ironfish-cli/src/commands/chain/status.ts b/ironfish-cli/src/commands/chain/status.ts index 2c2efd9eda..b856b1f565 100644 --- a/ironfish-cli/src/commands/chain/status.ts +++ b/ironfish-cli/src/commands/chain/status.ts @@ -3,12 +3,18 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { FileUtils, renderNetworkName } from '@ironfish/sdk' import { IronfishCommand } from '../../command' +import { ColorFlag, ColorFlagKey } from '../../flags' import * as ui from '../../ui' export default class ChainStatus extends IronfishCommand { static description = 'show chain information' + static enableJsonFlag = true - async start(): Promise { + static flags = { + [ColorFlagKey]: ColorFlag, + } + + async start(): Promise { const client = await this.connectRpc() const [status, difficulty, power] = await Promise.all([ @@ -30,5 +36,15 @@ export default class ChainStatus extends IronfishCommand { Power: FileUtils.formatHashRate(power.content.hashesPerSecond), }), ) + + return { + blockchain: status.content.blockchain, + difficulty: difficulty.content, + power: power.content, + node: { + network: renderNetworkName(status.content.node.networkId), + networkId: status.content.node.networkId, + }, + } } } From 79fbe891e121021fbff3150100918f71b969a710 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Mon, 29 Jul 2024 18:05:21 -0700 Subject: [PATCH 79/81] Add --json support to chain:transactions:info cmd (#5189) --- ironfish-cli/src/commands/chain/transactions/info.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ironfish-cli/src/commands/chain/transactions/info.ts b/ironfish-cli/src/commands/chain/transactions/info.ts index 21c301205c..6dfecd004b 100644 --- a/ironfish-cli/src/commands/chain/transactions/info.ts +++ b/ironfish-cli/src/commands/chain/transactions/info.ts @@ -5,14 +5,16 @@ import { CurrencyUtils, FileUtils } from '@ironfish/sdk' import { Args } from '@oclif/core' import { IronfishCommand } from '../../../command' -import { RemoteFlags } from '../../../flags' +import { ColorFlag, ColorFlagKey, RemoteFlags } from '../../../flags' import * as ui from '../../../ui' export class TransactionInfo extends IronfishCommand { static description = 'show transaction information' + static enableJsonFlag = true static flags = { ...RemoteFlags, + [ColorFlagKey]: ColorFlag, } static args = { @@ -22,7 +24,7 @@ export class TransactionInfo extends IronfishCommand { }), } - async start(): Promise { + async start(): Promise { const { args } = await this.parse(TransactionInfo) const client = await this.connectRpc() @@ -46,5 +48,7 @@ export class TransactionInfo extends IronfishCommand { 'Burn count': transaction.burns.length, }), ) + + return transaction } } From c3f6843b420452ef8cabd281b466d421d714b2c3 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Mon, 29 Jul 2024 18:18:47 -0700 Subject: [PATCH 80/81] Make chain:getTransaction use rpcSerializer (#5190) It already returned RpcTransaction with extra fields, so we can just use the serializer and attach these extra fields. --- .../src/rpc/routes/chain/getTransaction.ts | 57 ++++--------------- 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/ironfish/src/rpc/routes/chain/getTransaction.ts b/ironfish/src/rpc/routes/chain/getTransaction.ts index 9f2c2d80a7..f288f2bfdf 100644 --- a/ironfish/src/rpc/routes/chain/getTransaction.ts +++ b/ironfish/src/rpc/routes/chain/getTransaction.ts @@ -3,13 +3,12 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import * as yup from 'yup' import { Assert } from '../../../assert' -import { getTransactionSize } from '../../../network/utils/serializers' import { FullNode } from '../../../node' import { BlockHashSerdeInstance } from '../../../serde' -import { CurrencyUtils } from '../../../utils' import { RpcNotFoundError, RpcValidationError } from '../../adapters' import { ApiNamespace } from '../namespaces' import { routes } from '../router' +import { serializeRpcTransaction } from './serializers' import { RpcTransaction, RpcTransactionSchema } from './types' export type GetTransactionRequest = { transactionHash: string; blockHash?: string } @@ -82,57 +81,25 @@ routes.register( const transactions = await context.chain.getBlockTransactions(blockHeader) - const foundTransaction = transactions.find(({ transaction }) => + const chainTransaction = transactions.find(({ transaction }) => transaction.hash().equals(transactionHashBuffer), ) - if (!foundTransaction) { + if (!chainTransaction) { throw new RpcNotFoundError( `Transaction not found on block ${blockHashBuffer.toString('hex')}`, ) } - const { transaction, initialNoteIndex } = foundTransaction - - const rawTransaction: GetTransactionResponse = { + request.end({ + ...serializeRpcTransaction(chainTransaction.transaction, true), blockHash: blockHashBuffer.toString('hex'), - fee: Number(transaction.fee()), - expiration: transaction.expiration(), - hash: transaction.hash().toString('hex'), - size: getTransactionSize(transaction), - noteSize: initialNoteIndex + transaction.notes.length, - notesCount: transaction.notes.length, - spendsCount: transaction.spends.length, - signature: transaction.transactionSignature().toString('hex'), - notesEncrypted: transaction.notes.map((note) => note.serialize().toString('hex')), - notes: transaction.notes.map((note) => ({ - commitment: note.hash().toString('hex'), - hash: note.hash().toString('hex'), - serialized: note.serialize().toString('hex'), - })), - mints: transaction.mints.map((mint) => ({ - assetId: mint.asset.id().toString('hex'), - id: mint.asset.id().toString('hex'), - assetName: mint.asset.name().toString('hex'), - value: CurrencyUtils.encode(mint.value), - name: mint.asset.name().toString('hex'), - metadata: mint.asset.metadata().toString('hex'), - creator: mint.asset.creator().toString('hex'), - transferOwnershipTo: mint.transferOwnershipTo?.toString('hex'), - })), - burns: transaction.burns.map((burn) => ({ - assetId: burn.assetId.toString('hex'), - id: burn.assetId.toString('hex'), - assetName: '', - value: CurrencyUtils.encode(burn.value), - })), - spends: transaction.spends.map((spend) => ({ - nullifier: spend.nullifier.toString('hex'), - commitment: spend.commitment.toString('hex'), - size: spend.size, - })), - } - - request.end(rawTransaction) + noteSize: chainTransaction.initialNoteIndex + chainTransaction.transaction.notes.length, + notesCount: chainTransaction.transaction.notes.length, + spendsCount: chainTransaction.transaction.spends.length, + notesEncrypted: chainTransaction.transaction.notes.map((note) => + note.serialize().toString('hex'), + ), + }) }, ) From db5ea25e45eb03c1a5b3e4b4f0a57c3bbde2ef09 Mon Sep 17 00:00:00 2001 From: Rahul Patni Date: Thu, 1 Aug 2024 10:16:22 -0700 Subject: [PATCH 81/81] version bump for v2.5 (#5192) --- ironfish-cli/package.json | 6 +++--- ironfish-rust-nodejs/npm/darwin-arm64/package.json | 2 +- ironfish-rust-nodejs/npm/darwin-x64/package.json | 2 +- ironfish-rust-nodejs/npm/linux-arm64-gnu/package.json | 2 +- ironfish-rust-nodejs/npm/linux-arm64-musl/package.json | 2 +- ironfish-rust-nodejs/npm/linux-x64-gnu/package.json | 2 +- ironfish-rust-nodejs/npm/linux-x64-musl/package.json | 2 +- ironfish-rust-nodejs/npm/win32-x64-msvc/package.json | 2 +- ironfish-rust-nodejs/package.json | 2 +- ironfish/package.json | 4 ++-- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ironfish-cli/package.json b/ironfish-cli/package.json index babd2a7cd9..a33ae829c6 100644 --- a/ironfish-cli/package.json +++ b/ironfish-cli/package.json @@ -1,6 +1,6 @@ { "name": "ironfish", - "version": "2.4.1", + "version": "2.5.0", "description": "CLI for running and interacting with an Iron Fish node", "author": "Iron Fish (https://ironfish.network)", "main": "build/src/index.js", @@ -59,8 +59,8 @@ "oclif:version": "oclif readme && git add README.md" }, "dependencies": { - "@ironfish/rust-nodejs": "2.4.0", - "@ironfish/sdk": "2.4.1", + "@ironfish/rust-nodejs": "2.5.0", + "@ironfish/sdk": "2.5.0", "@ledgerhq/hw-transport-node-hid": "6.29.1", "@oclif/core": "4.0.11", "@oclif/plugin-autocomplete": "3.1.6", diff --git a/ironfish-rust-nodejs/npm/darwin-arm64/package.json b/ironfish-rust-nodejs/npm/darwin-arm64/package.json index 8fd800a807..b7c2e79307 100644 --- a/ironfish-rust-nodejs/npm/darwin-arm64/package.json +++ b/ironfish-rust-nodejs/npm/darwin-arm64/package.json @@ -1,6 +1,6 @@ { "name": "@ironfish/rust-nodejs-darwin-arm64", - "version": "2.4.0", + "version": "2.5.0", "os": [ "darwin" ], diff --git a/ironfish-rust-nodejs/npm/darwin-x64/package.json b/ironfish-rust-nodejs/npm/darwin-x64/package.json index 6fb392ab79..37a498d4c4 100644 --- a/ironfish-rust-nodejs/npm/darwin-x64/package.json +++ b/ironfish-rust-nodejs/npm/darwin-x64/package.json @@ -1,6 +1,6 @@ { "name": "@ironfish/rust-nodejs-darwin-x64", - "version": "2.4.0", + "version": "2.5.0", "os": [ "darwin" ], diff --git a/ironfish-rust-nodejs/npm/linux-arm64-gnu/package.json b/ironfish-rust-nodejs/npm/linux-arm64-gnu/package.json index bbea495fea..e137fa5f06 100644 --- a/ironfish-rust-nodejs/npm/linux-arm64-gnu/package.json +++ b/ironfish-rust-nodejs/npm/linux-arm64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@ironfish/rust-nodejs-linux-arm64-gnu", - "version": "2.4.0", + "version": "2.5.0", "os": [ "linux" ], diff --git a/ironfish-rust-nodejs/npm/linux-arm64-musl/package.json b/ironfish-rust-nodejs/npm/linux-arm64-musl/package.json index 30cb9421ba..49287e2e3c 100644 --- a/ironfish-rust-nodejs/npm/linux-arm64-musl/package.json +++ b/ironfish-rust-nodejs/npm/linux-arm64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@ironfish/rust-nodejs-linux-arm64-musl", - "version": "2.4.0", + "version": "2.5.0", "os": [ "linux" ], diff --git a/ironfish-rust-nodejs/npm/linux-x64-gnu/package.json b/ironfish-rust-nodejs/npm/linux-x64-gnu/package.json index b8f1f64a39..33b833b861 100644 --- a/ironfish-rust-nodejs/npm/linux-x64-gnu/package.json +++ b/ironfish-rust-nodejs/npm/linux-x64-gnu/package.json @@ -1,6 +1,6 @@ { "name": "@ironfish/rust-nodejs-linux-x64-gnu", - "version": "2.4.0", + "version": "2.5.0", "os": [ "linux" ], diff --git a/ironfish-rust-nodejs/npm/linux-x64-musl/package.json b/ironfish-rust-nodejs/npm/linux-x64-musl/package.json index 66133c58b3..07598dd27a 100644 --- a/ironfish-rust-nodejs/npm/linux-x64-musl/package.json +++ b/ironfish-rust-nodejs/npm/linux-x64-musl/package.json @@ -1,6 +1,6 @@ { "name": "@ironfish/rust-nodejs-linux-x64-musl", - "version": "2.4.0", + "version": "2.5.0", "os": [ "linux" ], diff --git a/ironfish-rust-nodejs/npm/win32-x64-msvc/package.json b/ironfish-rust-nodejs/npm/win32-x64-msvc/package.json index 425746cd12..a5a8b11320 100644 --- a/ironfish-rust-nodejs/npm/win32-x64-msvc/package.json +++ b/ironfish-rust-nodejs/npm/win32-x64-msvc/package.json @@ -1,6 +1,6 @@ { "name": "@ironfish/rust-nodejs-win32-x64-msvc", - "version": "2.4.0", + "version": "2.5.0", "os": [ "win32" ], diff --git a/ironfish-rust-nodejs/package.json b/ironfish-rust-nodejs/package.json index 8282c3ef0c..49a8e9ddce 100644 --- a/ironfish-rust-nodejs/package.json +++ b/ironfish-rust-nodejs/package.json @@ -1,6 +1,6 @@ { "name": "@ironfish/rust-nodejs", - "version": "2.4.0", + "version": "2.5.0", "description": "Node.js bindings for Rust code required by the Iron Fish SDK", "main": "index.js", "types": "index.d.ts", diff --git a/ironfish/package.json b/ironfish/package.json index 9699fb6145..813a98b780 100644 --- a/ironfish/package.json +++ b/ironfish/package.json @@ -1,6 +1,6 @@ { "name": "@ironfish/sdk", - "version": "2.4.1", + "version": "2.5.0", "description": "SDK for running and interacting with an Iron Fish node", "author": "Iron Fish (https://ironfish.network)", "main": "build/src/index.js", @@ -22,7 +22,7 @@ "dependencies": { "@ethersproject/bignumber": "5.7.0", "@fast-csv/format": "4.3.5", - "@ironfish/rust-nodejs": "2.4.0", + "@ironfish/rust-nodejs": "2.5.0", "@napi-rs/blake-hash": "1.3.3", "axios": "1.7.2", "bech32": "2.0.0",