From 68e739dd3fe947ba32fb1bf5838ae713481fd164 Mon Sep 17 00:00:00 2001 From: Daniel Cogan Date: Sat, 22 Apr 2023 07:47:20 -0700 Subject: [PATCH 01/19] Separate telemetry by endpoint (#3816) --- ironfish/src/fileStores/config.ts | 2 +- ironfish/src/node.ts | 6 ++---- ironfish/src/sdk.test.ts | 5 ----- ironfish/src/telemetry/telemetry.test.ts | 3 +++ ironfish/src/telemetry/telemetry.ts | 12 +++++++++++- ironfish/src/workerPool/pool.ts | 4 ++-- .../workerPool/tasks/submitTelemetry.test.ts | 17 ++++++++++++----- .../src/workerPool/tasks/submitTelemetry.ts | 15 ++++++++++++--- 8 files changed, 43 insertions(+), 21 deletions(-) diff --git a/ironfish/src/fileStores/config.ts b/ironfish/src/fileStores/config.ts index 4bfb2e5ae7..1269265fe5 100644 --- a/ironfish/src/fileStores/config.ts +++ b/ironfish/src/fileStores/config.ts @@ -403,7 +403,7 @@ export class Config extends KeyStore { confirmations: 2, minPeers: 1, targetPeers: 50, - telemetryApi: 'https://api.ironfish.network/telemetry', + telemetryApi: '', generateNewIdentity: false, blocksPerMessage: 25, minerBatchSize: 25000, diff --git a/ironfish/src/node.ts b/ironfish/src/node.ts index 2233fa108c..16934f98c6 100644 --- a/ironfish/src/node.ts +++ b/ironfish/src/node.ts @@ -113,14 +113,12 @@ export class IronfishNode { metrics, workerPool, localPeerIdentity: privateIdentityToIdentity(identity), - defaultTags: [ - { name: 'version', value: pkg.version }, - { name: 'networkId', value: networkId.toString() }, - ], + defaultTags: [{ name: 'version', value: pkg.version }], defaultFields: [ { name: 'node_id', type: 'string', value: internal.get('telemetryNodeId') }, { name: 'session_id', type: 'string', value: uuid() }, ], + networkId, }) this.peerNetwork = new PeerNetwork({ diff --git a/ironfish/src/sdk.test.ts b/ironfish/src/sdk.test.ts index fb6a3ca21d..bd359f058c 100644 --- a/ironfish/src/sdk.test.ts +++ b/ironfish/src/sdk.test.ts @@ -78,11 +78,6 @@ describe('IronfishSdk', () => { const sdk = await IronfishSdk.init({ configName: 'foo.config.json', fileSystem: fileSystem, - configOverrides: { - // TODO: It should be possible to test on the default network (mainnet) - // once the genesis block has been added. - networkId: 2, - }, }) const expectedDir = fileSystem.resolve(DEFAULT_DATA_DIR) diff --git a/ironfish/src/telemetry/telemetry.test.ts b/ironfish/src/telemetry/telemetry.test.ts index 9bcdab2f1d..60a9bf7c8f 100644 --- a/ironfish/src/telemetry/telemetry.test.ts +++ b/ironfish/src/telemetry/telemetry.test.ts @@ -28,6 +28,7 @@ describe('Telemetry', () => { beforeEach(() => { telemetry = new Telemetry({ + networkId: 2, chain: mockChain(), workerPool: mockWorkerPool(), config: mockConfig({ blockGraffiti: mockGraffiti }), @@ -55,6 +56,7 @@ describe('Telemetry', () => { describe('when disabled', () => { it('does nothing', () => { const disabledTelemetry = new Telemetry({ + networkId: 2, chain: mockChain(), workerPool: mockWorkerPool(), config: mockConfig({ blockGraffiti: mockGraffiti }), @@ -136,6 +138,7 @@ describe('Telemetry', () => { expect(submitTelemetry).toHaveBeenCalledWith( points.slice(0, telemetry['MAX_POINTS_TO_SUBMIT']), GraffitiUtils.fromString(mockGraffiti), + 'https://api.ironfish.network', ) expect(telemetry['points']).toEqual(points.slice(telemetry['MAX_POINTS_TO_SUBMIT'])) expect(telemetry['points']).toHaveLength( diff --git a/ironfish/src/telemetry/telemetry.ts b/ironfish/src/telemetry/telemetry.ts index d1d453cc43..003b53ce42 100644 --- a/ironfish/src/telemetry/telemetry.ts +++ b/ironfish/src/telemetry/telemetry.ts @@ -32,6 +32,7 @@ export class Telemetry { private readonly metrics: MetricsMonitor | null private readonly workerPool: WorkerPool private readonly localPeerIdentity: Identity + private readonly apiUrl: string private started: boolean private flushInterval: SetIntervalToken | null @@ -49,6 +50,7 @@ export class Telemetry { localPeerIdentity: Identity defaultFields?: Field[] defaultTags?: Tag[] + networkId: number }) { this.chain = options.chain this.workerPool = options.workerPool @@ -56,6 +58,7 @@ export class Telemetry { this.logger = options.logger ?? createRootLogger() this.metrics = options.metrics ?? null this.defaultTags = options.defaultTags ?? [] + this.defaultTags.push({ name: 'networkId', value: options.networkId.toString() }) this.defaultFields = options.defaultFields ?? [] this.localPeerIdentity = options.localPeerIdentity @@ -65,6 +68,13 @@ export class Telemetry { this.retries = 0 this._submitted = 0 this.started = false + + this.apiUrl = this.config.get('telemetryApi') + if (!this.apiUrl && options.networkId === 0) { + this.apiUrl = 'https://testnet.api.ironfish.network' + } else if (!this.apiUrl) { + this.apiUrl = 'https://api.ironfish.network' + } } get pending(): number { @@ -330,7 +340,7 @@ export class Telemetry { try { const graffiti = GraffitiUtils.fromString(this.config.get('blockGraffiti')) - await this.workerPool.submitTelemetry(points, graffiti) + await this.workerPool.submitTelemetry(points, graffiti, this.apiUrl) this.logger.debug(`Submitted ${points.length} telemetry points`) this.retries = 0 this._submitted += points.length diff --git a/ironfish/src/workerPool/pool.ts b/ironfish/src/workerPool/pool.ts index 2b0194b29d..fc3a5fe5b4 100644 --- a/ironfish/src/workerPool/pool.ts +++ b/ironfish/src/workerPool/pool.ts @@ -208,8 +208,8 @@ export class WorkerPool { return job } - async submitTelemetry(points: Metric[], graffiti: Buffer): Promise { - const request = new SubmitTelemetryRequest(points, graffiti) + async submitTelemetry(points: Metric[], graffiti: Buffer, apiHost: string): Promise { + const request = new SubmitTelemetryRequest(points, graffiti, apiHost) await this.execute(request).result() } diff --git a/ironfish/src/workerPool/tasks/submitTelemetry.test.ts b/ironfish/src/workerPool/tasks/submitTelemetry.test.ts index ae156f8c85..93d66901d8 100644 --- a/ironfish/src/workerPool/tasks/submitTelemetry.test.ts +++ b/ironfish/src/workerPool/tasks/submitTelemetry.test.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 axios from 'axios' import { Metric } from '../../telemetry' import { BufferUtils, GraffitiUtils } from '../../utils' -import { WebApi } from '../../webApi' import { SubmitTelemetryRequest, SubmitTelemetryResponse, @@ -42,7 +42,11 @@ describe('SubmitTelemetryRequest', () => { timestamp: new Date(), } - const request = new SubmitTelemetryRequest([mockMetric], GraffitiUtils.fromString('')) + const request = new SubmitTelemetryRequest( + [mockMetric], + GraffitiUtils.fromString(''), + 'mock.api.endpoint', + ) const buffer = request.serialize() const deserializedRequest = SubmitTelemetryRequest.deserialize(request.jobId, buffer) expect(deserializedRequest).toEqual(request) @@ -61,7 +65,7 @@ describe('SubmitTelemetryTask', () => { describe('execute', () => { it('submits points to the API', async () => { const submitTelemetryPointsToApi = jest - .spyOn(WebApi.prototype, 'submitTelemetry') + .spyOn(axios, 'post') .mockImplementationOnce(jest.fn()) const mockMetric: Metric = { measurement: 'node', @@ -78,10 +82,13 @@ describe('SubmitTelemetryTask', () => { const graffitiBuffer = GraffitiUtils.fromString('testgraffiti') const graffiti = BufferUtils.toHuman(graffitiBuffer) const task = new SubmitTelemetryTask() - const request = new SubmitTelemetryRequest(points, graffitiBuffer) + const request = new SubmitTelemetryRequest(points, graffitiBuffer, 'mock.api.endpoint') await task.execute(request) - expect(submitTelemetryPointsToApi).toHaveBeenCalledWith({ points, graffiti }) + expect(submitTelemetryPointsToApi).toHaveBeenCalledWith('mock.api.endpoint/telemetry', { + points, + graffiti, + }) }) }) }) diff --git a/ironfish/src/workerPool/tasks/submitTelemetry.ts b/ironfish/src/workerPool/tasks/submitTelemetry.ts index 32121cc34e..74652621fa 100644 --- a/ironfish/src/workerPool/tasks/submitTelemetry.ts +++ b/ironfish/src/workerPool/tasks/submitTelemetry.ts @@ -12,16 +12,20 @@ import { WorkerTask } from './workerTask' export class SubmitTelemetryRequest extends WorkerMessage { readonly points: Metric[] readonly graffiti: Buffer + readonly apiHost: string - constructor(points: Metric[], graffiti: Buffer, jobId?: number) { + constructor(points: Metric[], graffiti: Buffer, apiHost: string, jobId?: number) { super(WorkerMessageType.SubmitTelemetry, jobId) this.points = points this.graffiti = graffiti + this.apiHost = apiHost } serialize(): Buffer { const bw = bufio.write(this.getSize()) bw.writeVarBytes(this.graffiti) + bw.writeVarString(this.apiHost, 'utf8') + bw.writeU64(this.points.length) for (const point of this.points) { @@ -64,6 +68,8 @@ export class SubmitTelemetryRequest extends WorkerMessage { static deserialize(jobId: number, buffer: Buffer): SubmitTelemetryRequest { const reader = bufio.read(buffer, true) const graffiti = reader.readVarBytes() + const apiHost = reader.readVarString('utf8') + const pointsLength = reader.readU64() const points = [] for (let i = 0; i < pointsLength; i++) { @@ -114,11 +120,13 @@ export class SubmitTelemetryRequest extends WorkerMessage { points.push({ measurement, tags, timestamp, fields }) } - return new SubmitTelemetryRequest(points, graffiti, jobId) + return new SubmitTelemetryRequest(points, graffiti, apiHost, jobId) } getSize(): number { let size = 8 + bufio.sizeVarBytes(this.graffiti) + size += bufio.sizeVarString(this.apiHost, 'utf8') + for (const point of this.points) { size += bufio.sizeVarString(point.measurement, 'utf8') size += bufio.sizeVarString(point.timestamp.toISOString(), 'utf8') @@ -186,8 +194,9 @@ export class SubmitTelemetryTask extends WorkerTask { jobId, points, graffiti, + apiHost, }: SubmitTelemetryRequest): Promise { - const api = new WebApi() + const api = new WebApi({ host: apiHost }) await api.submitTelemetry({ points, graffiti: BufferUtils.toHuman(graffiti) }) return new SubmitTelemetryResponse(jobId) } From 3b1735ef3a7287f611b6e9a5f5f7484d647e26a8 Mon Sep 17 00:00:00 2001 From: austin <17057747+holahula@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:17:07 -0400 Subject: [PATCH 02/19] feat(ironfish): add note hash to account notes stream rpc (#3831) --- ironfish-cli/src/commands/wallet/notes.ts | 3 +++ ironfish/src/rpc/routes/wallet/getNotes.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/ironfish-cli/src/commands/wallet/notes.ts b/ironfish-cli/src/commands/wallet/notes.ts index 2229a2c4d5..db2b9cb500 100644 --- a/ironfish-cli/src/commands/wallet/notes.ts +++ b/ironfish-cli/src/commands/wallet/notes.ts @@ -46,6 +46,9 @@ export class NotesCommand extends IronfishCommand { sender: { header: 'Sender', }, + noteHash: { + header: 'Note Hash', + }, transactionHash: { header: 'From Transaction', }, diff --git a/ironfish/src/rpc/routes/wallet/getNotes.ts b/ironfish/src/rpc/routes/wallet/getNotes.ts index 7653b393bd..9ef383fe00 100644 --- a/ironfish/src/rpc/routes/wallet/getNotes.ts +++ b/ironfish/src/rpc/routes/wallet/getNotes.ts @@ -14,6 +14,7 @@ export type GetAccountNotesStreamResponse = { assetName: string memo: string sender: string + noteHash: string transactionHash: string spent: boolean | undefined } @@ -33,6 +34,7 @@ export const GetAccountNotesStreamResponseSchema: yup.ObjectSchema Date: Mon, 24 Apr 2023 12:51:04 -0700 Subject: [PATCH 03/19] Rpc spend (#3832) --- ironfish/src/rpc/routes/wallet/types.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ironfish/src/rpc/routes/wallet/types.ts b/ironfish/src/rpc/routes/wallet/types.ts index 3a92eeea85..be24014fb3 100644 --- a/ironfish/src/rpc/routes/wallet/types.ts +++ b/ironfish/src/rpc/routes/wallet/types.ts @@ -32,3 +32,9 @@ export type RpcAccountDecryptedNote = { owner: string spent: boolean } + +export type RpcSpend = { + nullifier: string + commitment: string + size: number +} From 0060f406c23a5f54d9818a9bc69016bd0120a7ec Mon Sep 17 00:00:00 2001 From: Rohan Jadvani <5459049+rohanjadvani@users.noreply.github.com> Date: Mon, 24 Apr 2023 17:47:28 -0400 Subject: [PATCH 04/19] feat(ironfish): Add missing note hashes from RPC responses (#3836) --- .../rpc/routes/chain/getTransaction.test.ts | 12 ++++++++++ .../src/rpc/routes/chain/getTransaction.ts | 23 ++++++++++++++++++- .../rpc/routes/chain/getTransactionStream.ts | 3 +++ .../src/rpc/routes/wallet/getTransaction.ts | 1 + .../src/rpc/routes/wallet/getTransactions.ts | 1 + ironfish/src/rpc/routes/wallet/types.ts | 1 + ironfish/src/rpc/routes/wallet/utils.ts | 1 + 7 files changed, 41 insertions(+), 1 deletion(-) diff --git a/ironfish/src/rpc/routes/chain/getTransaction.test.ts b/ironfish/src/rpc/routes/chain/getTransaction.test.ts index b7b57eb2c9..2495d8cd46 100644 --- a/ironfish/src/rpc/routes/chain/getTransaction.test.ts +++ b/ironfish/src/rpc/routes/chain/getTransaction.test.ts @@ -19,9 +19,14 @@ describe('Route chain/getTransaction', () => { const transaction = block2.transactions[0] const notesEncrypted: string[] = [] + const notes: { hash: string; serialized: string }[] = [] for (const note of transaction.notes) { notesEncrypted.push(note.serialize().toString('hex')) + notes.push({ + hash: note.hash().toString('hex'), + serialized: note.serialize().toString('hex'), + }) } const response = await routeTest.client @@ -39,6 +44,7 @@ describe('Route chain/getTransaction', () => { notesEncrypted, mints: [], burns: [], + notes, }) }) @@ -51,9 +57,14 @@ describe('Route chain/getTransaction', () => { const transaction = block2.transactions[0] const notesEncrypted: string[] = [] + const notes: { hash: string; serialized: string }[] = [] for (const note of transaction.notes) { notesEncrypted.push(note.serialize().toString('hex')) + notes.push({ + hash: note.hash().toString('hex'), + serialized: note.serialize().toString('hex'), + }) } const response = await routeTest.client @@ -72,6 +83,7 @@ describe('Route chain/getTransaction', () => { notesEncrypted, mints: [], burns: [], + notes, }) }) diff --git a/ironfish/src/rpc/routes/chain/getTransaction.ts b/ironfish/src/rpc/routes/chain/getTransaction.ts index c0d6da188e..dce1a89fd2 100644 --- a/ironfish/src/rpc/routes/chain/getTransaction.ts +++ b/ironfish/src/rpc/routes/chain/getTransaction.ts @@ -16,7 +16,10 @@ export type GetTransactionResponse = { notesCount: number spendsCount: number signature: string - notesEncrypted: string[] + notes: { + hash: string + serialized: string + }[] mints: { assetId: string value: string @@ -25,6 +28,8 @@ export type GetTransactionResponse = { assetId: string value: string }[] + // Deprecated: use `notes` instead + notesEncrypted: string[] } export const GetTransactionRequestSchema: yup.ObjectSchema = yup .object({ @@ -42,6 +47,16 @@ export const GetTransactionResponseSchema: yup.ObjectSchema( spendsCount: 0, signature: '', notesEncrypted: [], + notes: [], mints: [], burns: [], } @@ -123,6 +139,11 @@ router.register( rawTransaction.signature = signature.toString('hex') rawTransaction.notesEncrypted = notesEncrypted + rawTransaction.notes = transaction.notes.map((note) => ({ + hash: note.hash().toString('hex'), + serialized: note.serialize().toString('hex'), + })) + rawTransaction.mints = transaction.mints.map((mint) => ({ assetId: mint.asset.id().toString('hex'), value: CurrencyUtils.encode(mint.value), diff --git a/ironfish/src/rpc/routes/chain/getTransactionStream.ts b/ironfish/src/rpc/routes/chain/getTransactionStream.ts index 61829ff542..401bf8761b 100644 --- a/ironfish/src/rpc/routes/chain/getTransactionStream.ts +++ b/ironfish/src/rpc/routes/chain/getTransactionStream.ts @@ -15,6 +15,7 @@ import { ApiNamespace, router } from '../router' interface Note { assetId: string assetName: string + hash: string value: string memo: string } @@ -42,6 +43,7 @@ const NoteSchema = yup .shape({ assetId: yup.string().required(), assetName: yup.string().required(), + hash: yup.string().required(), value: yup.string().required(), memo: yup.string().required(), }) @@ -163,6 +165,7 @@ router.register Date: Mon, 24 Apr 2023 18:22:41 -0400 Subject: [PATCH 05/19] fix(simulator): gracefully shutdown simulator on unexpected exit (#3773) * fix: gracefully shutdown simulator on unexpected exit * make simulator a normal const in start cmd --- simulator/src/commands/start.ts | 19 ++++++++++++++++--- simulator/src/simulations/index.ts | 9 ++------- simulator/src/simulations/send.ts | 7 +------ simulator/src/simulations/stability.ts | 4 +--- simulator/src/simulator/simulator.ts | 19 +++++-------------- 5 files changed, 25 insertions(+), 33 deletions(-) diff --git a/simulator/src/commands/start.ts b/simulator/src/commands/start.ts index 12ca032006..01ec64f591 100644 --- a/simulator/src/commands/start.ts +++ b/simulator/src/commands/start.ts @@ -5,6 +5,7 @@ import { createRootLogger } from '@ironfish/sdk' import { CliUx, Command, Config } from '@oclif/core' import { Flags } from '@oclif/core' import { SIMULATIONS } from '../simulations' +import { Simulator } from '../simulator' export abstract class Start extends Command { static description = 'Start a simulation' @@ -58,8 +59,20 @@ export abstract class Start extends Command { // If you want logs to persist, i.e. via `simulator start 1 2>&1 | tee ~/i/logs/run_1.log` you will // need to remove the spinner CliUx.ux.action.start(`running simulation ${simName}`) - await simulation.run(logger, { persist, duration }) - CliUx.ux.action.start(`stop simulation ${simName}`) - this.exit() + + // The simulator is created here because oclif catches errors so we can't throw them + // and handle `uncaughtException` in the simulator. Having this try-catch block is a workaround + // to ensure the simulator gracefully exits when an error occurs. + const simulator = new Simulator(logger, { persist, duration }) + + try { + await simulation.run(simulator, logger) + } catch (e) { + logger.error(`simulation encountered ${String(e)}, shutting down...`) + simulator.exit(1) + } + + CliUx.ux.action.stop(`stop simulation ${simName}`) + this.exit(0) } } diff --git a/simulator/src/simulations/index.ts b/simulator/src/simulations/index.ts index e7d9ee9c26..41d7abd367 100644 --- a/simulator/src/simulations/index.ts +++ b/simulator/src/simulations/index.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 { Logger } from '@ironfish/sdk' +import { Simulator } from '../simulator' import * as send from './send' import * as stability from './stability' @@ -9,13 +10,7 @@ import * as stability from './stability' * Interface that simulations must implement to be run by the framework. */ export interface Simulation { - run( - logger: Logger, - options?: { - persist?: boolean - duration?: number - }, - ): Promise + run(simulator: Simulator, logger: Logger): Promise } /** diff --git a/simulator/src/simulations/send.ts b/simulator/src/simulations/send.ts index 614fd511db..501bb0cc40 100644 --- a/simulator/src/simulations/send.ts +++ b/simulator/src/simulations/send.ts @@ -9,12 +9,7 @@ import { IRON, SECOND, sendTransaction, SimulationNode, Simulator, sleep } from // Author: holahula // Purpose: Send transactions from one node to another every 3 seconds -export async function run( - logger: Logger, - options?: { persist?: boolean; duration?: number }, -): Promise { - const simulator = new Simulator(logger, options) - +export async function run(simulator: Simulator, logger: Logger): Promise { const nodes = [] for (let i = 0; i < 2; i++) { nodes.push(await simulator.startNode()) diff --git a/simulator/src/simulations/stability.ts b/simulator/src/simulations/stability.ts index ab85225535..6797e9e5a7 100644 --- a/simulator/src/simulations/stability.ts +++ b/simulator/src/simulations/stability.ts @@ -19,9 +19,7 @@ import { // This simulation tests the stability of the network by randomly stopping and starting nodes, // trying to see if nodes crash over time. The memory usage of the nodes is also monitored. -export async function run(logger: Logger, options?: { persist: boolean }): Promise { - const simulator = new Simulator(logger, options) - +export async function run(simulator: Simulator, logger: Logger): Promise { const alive: Set = new Set() const onExit = (event: ExitEvent): void => { diff --git a/simulator/src/simulator/simulator.ts b/simulator/src/simulator/simulator.ts index ac1e70ad8e..518aa47e9e 100644 --- a/simulator/src/simulator/simulator.ts +++ b/simulator/src/simulator/simulator.ts @@ -69,19 +69,10 @@ export class Simulator { } } - process - .on('SIGINT', (event) => { - this.logger.log(`simulator handled ${event.toString()}`) - this.exit(1) - }) - .on('uncaughtException', (err) => { - this.logger.log(`simulator handled uncaught exception: ${String(err)}`) - this.exit(1) - }) - .on('unhandledRejection', (reason, _) => { - this.logger.log(`simulator handled unhandled rejection: ${String(reason)}`) - this.exit(1) - }) + process.on('SIGINT' || 'SIGKILL', (event) => { + this.logger.log(`simulator handled signal ${event.toString()}`) + this.exit(1) + }) } /** @@ -155,7 +146,7 @@ export class Simulator { * Unexpected process exit handler. * This deletes all data directories, kills all nodes, and exits the Simulator process. */ - private exit(code = 0) { + public exit(code = 0): void { this.nodes.forEach((node) => node.kill()) this.deleteDataDirs() this.logger.log('exiting...') From 6b50c4987cfdaf14193fa6b597f71822200c8ac3 Mon Sep 17 00:00:00 2001 From: ygao76 <4500784+ygao76@users.noreply.github.com> Date: Mon, 24 Apr 2023 16:03:41 -0700 Subject: [PATCH 06/19] Add spends to chain/getTransaction (#3838) * Add spends to chain/getTransaction * Resolve conflict --- .../rpc/routes/chain/getTransaction.test.ts | 15 ++++++++++++++ .../src/rpc/routes/chain/getTransaction.ts | 20 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/ironfish/src/rpc/routes/chain/getTransaction.test.ts b/ironfish/src/rpc/routes/chain/getTransaction.test.ts index 2495d8cd46..eebf7055f2 100644 --- a/ironfish/src/rpc/routes/chain/getTransaction.test.ts +++ b/ironfish/src/rpc/routes/chain/getTransaction.test.ts @@ -5,6 +5,7 @@ import { useMinerBlockFixture } from '../../../testUtilities' import { createRouteTest } from '../../../testUtilities/routeTest' import { CurrencyUtils } from '../../../utils' import { RpcRequestError } from '../../clients' +import { RpcSpend } from '../wallet/types' import { GetTransactionResponse } from './getTransaction' describe('Route chain/getTransaction', () => { @@ -35,6 +36,12 @@ describe('Route chain/getTransaction', () => { }) .waitForEnd() + const spends: RpcSpend[] = transaction.spends.map((spend) => ({ + nullifier: spend.nullifier.toString('hex'), + commitment: spend.commitment.toString('hex'), + size: spend.size, + })) + expect(response.content).toMatchObject({ fee: CurrencyUtils.encode(transaction.fee()), expiration: transaction.expiration(), @@ -42,6 +49,7 @@ describe('Route chain/getTransaction', () => { spendsCount: 0, signature: transaction.transactionSignature().toString('hex'), notesEncrypted, + spends, mints: [], burns: [], notes, @@ -74,6 +82,12 @@ describe('Route chain/getTransaction', () => { }) .waitForEnd() + const spends: RpcSpend[] = transaction.spends.map((spend) => ({ + nullifier: spend.nullifier.toString('hex'), + commitment: spend.commitment.toString('hex'), + size: spend.size, + })) + expect(response.content).toMatchObject({ fee: CurrencyUtils.encode(transaction.fee()), expiration: transaction.expiration(), @@ -81,6 +95,7 @@ describe('Route chain/getTransaction', () => { spendsCount: 0, signature: transaction.transactionSignature().toString('hex'), notesEncrypted, + spends, mints: [], burns: [], notes, diff --git a/ironfish/src/rpc/routes/chain/getTransaction.ts b/ironfish/src/rpc/routes/chain/getTransaction.ts index dce1a89fd2..eac2850323 100644 --- a/ironfish/src/rpc/routes/chain/getTransaction.ts +++ b/ironfish/src/rpc/routes/chain/getTransaction.ts @@ -6,6 +6,7 @@ import { BlockHashSerdeInstance } from '../../../serde' import { CurrencyUtils } from '../../../utils' import { ValidationError } from '../../adapters' import { ApiNamespace, router } from '../router' +import { RpcSpend } from '../wallet/types' export type GetTransactionRequest = { transactionHash: string; blockHash?: string } @@ -16,6 +17,7 @@ export type GetTransactionResponse = { notesCount: number spendsCount: number signature: string + spends: RpcSpend[] notes: { hash: string serialized: string @@ -47,6 +49,17 @@ export const GetTransactionResponseSchema: yup.ObjectSchema( spendsCount: 0, signature: '', notesEncrypted: [], + spends: [], notes: [], mints: [], burns: [], @@ -139,6 +153,12 @@ router.register( rawTransaction.signature = signature.toString('hex') rawTransaction.notesEncrypted = notesEncrypted + rawTransaction.spends = transaction.spends.map((spend) => ({ + nullifier: spend.nullifier.toString('hex'), + commitment: spend.commitment.toString('hex'), + size: spend.size, + })) + rawTransaction.notes = transaction.notes.map((note) => ({ hash: note.hash().toString('hex'), serialized: note.serialize().toString('hex'), From 1c2c600bbfd755851b35181b5fc14d47eb120ec1 Mon Sep 17 00:00:00 2001 From: austin <17057747+holahula@users.noreply.github.com> Date: Mon, 24 Apr 2023 19:25:22 -0400 Subject: [PATCH 07/19] feat(ironfish): add spends to `wallet/getTransaction` (#3835) * feat(ironfish): add spends to wallet/getTransaction * feat(ironfish): add spend support to cli get txn command * test(ironfish) add unt test for wallet/getAccountTransaction * Update unit test --------- Co-authored-by: ygao76 --- .../src/commands/wallet/transaction/index.ts | 22 ++++++++- .../getTransaction.test.ts.fixture | 45 +++++++++++++++++++ .../rpc/routes/wallet/getTransaction.test.ts | 39 ++++++++++++++++ .../src/rpc/routes/wallet/getTransaction.ts | 21 ++++++++- 4 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 ironfish/src/rpc/routes/wallet/__fixtures__/getTransaction.test.ts.fixture create mode 100644 ironfish/src/rpc/routes/wallet/getTransaction.test.ts diff --git a/ironfish-cli/src/commands/wallet/transaction/index.ts b/ironfish-cli/src/commands/wallet/transaction/index.ts index 4c940ecb93..d9858e2967 100644 --- a/ironfish-cli/src/commands/wallet/transaction/index.ts +++ b/ironfish-cli/src/commands/wallet/transaction/index.ts @@ -59,7 +59,7 @@ export class TransactionCommand extends IronfishCommand { this.log(`Sender: ${response.content.transaction.notes[0].sender}`) if (response.content.transaction.notes.length > 0) { - this.log(`---Notes---\n`) + this.log(`\n---Notes---\n`) CliUx.ux.table(response.content.transaction.notes, { amount: { @@ -91,8 +91,26 @@ export class TransactionCommand extends IronfishCommand { }) } + if (response.content.transaction.spendsCount > 0) { + this.log(`\n---Spends---\n`) + CliUx.ux.table(response.content.transaction.spends, { + size: { + header: 'Size', + get: (spend) => spend.size, + }, + nullifier: { + header: 'Nullifier', + get: (spend) => spend.nullifier, + }, + commitmment: { + header: 'Commitment', + get: (spend) => spend.commitment, + }, + }) + } + if (response.content.transaction.assetBalanceDeltas) { - this.log(`---Asset Balance Deltas---\n`) + this.log(`\n---Asset Balance Deltas---\n`) CliUx.ux.table(response.content.transaction.assetBalanceDeltas, { assetId: { header: 'Asset ID', diff --git a/ironfish/src/rpc/routes/wallet/__fixtures__/getTransaction.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/getTransaction.test.ts.fixture new file mode 100644 index 0000000000..e9ea860705 --- /dev/null +++ b/ironfish/src/rpc/routes/wallet/__fixtures__/getTransaction.test.ts.fixture @@ -0,0 +1,45 @@ +{ + "Route wallet/getAccountTransaction gets transaction by account": [ + { + "version": 2, + "id": "0eb11031-e0c8-47bf-b653-51f1f6f3d80a", + "name": "account", + "spendingKey": "8c43e126f7f5158bfc50e10548118e92288936bc63ef806bd94bf6264eb2985e", + "viewKey": "ad0faf2dd74086123a4e5b40982975aba6a8e8ff3a45b9bdb56d2b0b18545641f4560dcd2e9340006d21df8c6d473ec47810695ff257e41524bea9cc13a3f20a", + "incomingViewKey": "894874a01056a4df1d6dd1f89bc846cd29da5371c12b3fe4d0359f928026b003", + "outgoingViewKey": "6ecbf4a57b45d1dc550fe1dafe01dd3bcc9224f6dd7d9badca25ae977f68eee2", + "publicAddress": "9ebd7fe8d5a8511b0695900e10493289f1decc3706369427ab879754485004bd", + "createdAt": null + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", + "noteCommitment": { + "type": "Buffer", + "data": "base64:8PYL4cItain96rVyrDKBsFLW752fvGtwa43otrhbkiE=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:DHdeVwmWwnLe28yAdksb40sucGQiySKZixuyJFaMAns=" + }, + "target": "883423532389192164791648750371459257913741948437809479060803100646309888", + "randomness": "0", + "timestamp": 1682372146792, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAhbdXRoiKFdbLX0/9XeS9gUXd9qSiORcsHGlAm6rUocWFRqOT6QWCLRPPSIAeShmw6GD+AErxUDeorrycBLBraJ+lnGwBoTEF1I7csP/0e26B8Mu9tyVxzaH+SqD9G5XfC5cU6m7hVchKTGXqMfHBVFvIx1V8NIHm5tifvtntC8IF9O2pmcICZfQ82zYKkaX4Jp/tsimI5Df+ZZWfS80hshwaXP1uXPaEa5dsqEUfSzKEXb71JYqlXRpngJy8XS55VFGLR+fLLT7TuFYPNQAQf9imPYnCjon6cdQ9xDW0L8AR1ywNjM6mwafG0DTZSi6XSDZ7h0jFor3Qgwlnd68TY781bi+7vpnaxhAPMUyaN9kj2KPEYmDTSyV/rx6SUygpXIODg/YU5v3SEMMw4UFEdqcKIVkcjHpyD9ypa7tSicp6I/FL/zplVOxNeH/38N9Gjbbq/jnVwp1GsHmV492SphbD4SgBwCMqQ6tgkn9P5J2qp4Bzcgqzc8O3ZK05vMa8K2ekNzr96WXW4J0xMKaCwBXUstj3UcBpghm2kWnYIIjSUwoiWMQBGWiOPbaCRIvXeUKJl5ej+G2xTOCmPzrvTIVSKa+/XgBgTrSy+hPOo8CXu9HYddix2klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwZKQAVaMq4gLSeKVWPbXNmZaJ5fxa2ntpN3VkErNOoJrmRmiZH5NjfYbRdZfvM8GPkpgINk7TIxEwXdxXk3lxDg==" + } + ] + }, + { + "type": "Buffer", + "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXg26j0TlAx3lFtKn+8SpzITIZtsRi42X9Xym5WXRplOv2Ii8zzXJ9ZD8MUhBlCCvAIvwPpSgC1YJwmyLzij2Ao0EE7TO2jAGnvWQiL9pTsmhDp65ve3lSm13yUTPsnrudYUoKv3DXqVsKlmsE2cD41DFwclhWVCfnYwf2WRqZzsLn2VsyyqUWL5tqtkjgPOuYaJGVsAfQnTeB/QXigrq5luwjlw3vCKZY++p7RopP06zMR2EvpTbzCOC7uK3dA+HjhpvSvdveSXXTgCaG1teCmYtg8cly63SlG8bnij7BccCXnwKIfBOGcQZZKxGlgdTe8N93EfCSwg9hHjcVzsn6fD2C+HCLWop/eq1cqwygbBS1u+dn7xrcGuN6La4W5IhBAAAALOg1/SORJsUWi32VxjIofXdICpeKE57ZCoEnFvng0bK/X9nrIHGl9en5jvHyePpG3bFdq/cV+EfWl1m1N8rhbO0/i+2g2Vwok39EqQPT7RS8kWu70OcpMPgvCXmrrY2DqCMVQGLICGfrzQN5TLklfoHDxTuhqloRBWJZPQtNIhk0RbhCpY3vHpIpOCjYk1oZrTS7ZKkO1jQnVS6Z4y+a2ABTPGPiyZVjn1DtJTTivGLKoCrmzGfjhDTemAEj3vFAwSTUQpzPZZDLxdDIjLf5c+g5r79bqwV/bkgwFyopfJ37ANZW38RWKznPlqYgeJraYH2H/thkGURVwneYFkgfpLer33NNQd/dMoGPAE+2Nehbn+wmvcRsMGIOKfnGiCpUv42t7RSu7dY+cWaxH8C+DJtlYlksJLcYzNDvgw45CblnVngiPav6gmRRvMxsPHxlQnWDWrfOV/gHAW4wsqPoAu+3fAP4CMxHhZkgU3sL+NU62N/mmJavWc7U9PoIveHPA0fBaqzf1glYL1bQ1GIOnI0pKMhz4AP6XZehhOSREYl4oKGF2X0vs/8+wBgrdlde8Bou1Vm+XRbdmS2Wk0/3noylfHBY+xFh8V7Qlxx0TtyfwREkHzAzmWdBmmP0M5RNvyAZN7uFC/45n12WRd2cDC2JHyg3GQM5LgBXewZuw6lLM5gDSGf0bvW+kgRmm/6+IpHIHYYCd8+Q8V9fzqSDATAmWK5vW5GmNFF6MYDtKlRFdHCkCtJoJvuRTFK5GH7CZBX1nZoHo2T9OjxbkwY5DvTsJohOz655u67IxzYmKGAzCvt6fZHed2SKD+XiQf5dOSz1LqaXwO7OYUjmm4+otI4Cj7Ls3bCBFBYh3+7FgCs248MVZ3ef+2PFfMfXtVN7cKfTjojniwlYk0yDA7gSTpB4jYgExOMs7ApKeSWF6KDkRV134rv48QGyV2eESXzF/Nc9tPRCcokcjtrfdNQTX8263i0lsXpbTjGDGtFeWCCj2+LPNh58wKoWMcNoTbrsPF7asTCUvKjvjKdn0CGKVfZMIpZtCfvRh+z7FrxEHv2h4do9rOjgdD42yJfBj5OnNJCrRouZYHXSTxh7GnR0dnyuT0AeBiT3EhDCND43CVPsQrBo4ALMuCnBgq8d3+Qg+DIncpbxDhyqgSUoaRPvMdrX4SL1kBlLTLx02zRhAKPW2cwgXPtd0rP0HI7WinpZNjtUzZCSlzm02Ykq2h55Xxo91JDBsiOlZxEdDy9hh5zaJYBTND3JJG1eWoQSrgxduh660+e1RK6F6Hx3oMzh5g/CC1q9HzVHDe70x4eXTHQqhpD0rrZZ9/EJQqcCZvrh39DMJ5RndPn8EKzYk0UTN40byYWprvKuM7wsxN/0rru1VhtbZQRnCi8n2p3nRJsbFjMWs/ZQyMgQQwoD5JCOxhfng6sRcBht1Ls9RabXLFs78RjpZ+yL5hiilkf0dyuZV9pCeNRNf5gyhG4t11TbOycE262QBB1JKUyFoAn+K53mnIWyPVIMCZCs+fl7MzX5A6rqYO1Fmyxup6NTANFIihwDPBWggM1loAr+XGkML8632oiMbHcfez/6dQ3sjueCw==" + } + ] +} \ No newline at end of file diff --git a/ironfish/src/rpc/routes/wallet/getTransaction.test.ts b/ironfish/src/rpc/routes/wallet/getTransaction.test.ts new file mode 100644 index 0000000000..b5e9537cf1 --- /dev/null +++ b/ironfish/src/rpc/routes/wallet/getTransaction.test.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 { Assert } from '../../../assert' +import { useAccountFixture, useTxSpendsFixture } from '../../../testUtilities' +import { createRouteTest } from '../../../testUtilities/routeTest' + +describe('Route wallet/getAccountTransaction', () => { + const routeTest = createRouteTest(true) + + it('gets transaction by account', async () => { + const node = routeTest.node + const account = await useAccountFixture(node.wallet, 'account') + + const { transaction } = await useTxSpendsFixture(node, { account }) + + const response = await routeTest.client.wallet.getAccountTransaction({ + hash: transaction.hash().toString('hex'), + account: account.name, + }) + + expect(response.status).toBe(200) + + const { transaction: responseTransaction, account: responseAccount } = response.content + + Assert.isNotNull(responseTransaction) + + expect(responseAccount).toMatch(account.name) + + expect(responseTransaction.spends).toEqual( + transaction.spends.map((spend) => ({ + nullifier: spend.nullifier.toString('hex'), + commitment: spend.commitment.toString('hex'), + size: spend.size, + })), + ) + expect(responseTransaction.notes).toHaveLength(transaction.notes.length) + }) +}) diff --git a/ironfish/src/rpc/routes/wallet/getTransaction.ts b/ironfish/src/rpc/routes/wallet/getTransaction.ts index 935ab24c72..dcd390b530 100644 --- a/ironfish/src/rpc/routes/wallet/getTransaction.ts +++ b/ironfish/src/rpc/routes/wallet/getTransaction.ts @@ -4,7 +4,7 @@ import * as yup from 'yup' import { TransactionStatus, TransactionType } from '../../../wallet' import { ApiNamespace, router } from '../router' -import { RpcAccountDecryptedNote } from './types' +import { RpcAccountDecryptedNote, RpcSpend } from './types' import { getAccount, getAccountDecryptedNotes, @@ -36,6 +36,7 @@ export type GetAccountTransactionResponse = { submittedSequence: number assetBalanceDeltas: Array<{ assetId: string; assetName: string; delta: string }> notes: RpcAccountDecryptedNote[] + spends: RpcSpend[] } | null } @@ -95,6 +96,17 @@ export const GetAccountTransactionResponseSchema: yup.ObjectSchema ({ + nullifier: spend.nullifier.toString('hex'), + commitment: spend.commitment.toString('hex'), + size: spend.size, + })) + const confirmations = request.data.confirmations ?? node.config.get('confirmations') const status = await node.wallet.getTransactionStatus(account, transaction, { @@ -135,6 +153,7 @@ router.register Date: Mon, 24 Apr 2023 17:22:26 -0700 Subject: [PATCH 08/19] adds custom transaction funding by note hash (#3830) * adds custom transaction funding by note hash supporting custom transaction funding will allow users and developers to choose the notes to spend in a transaction using whatever note selection algorithm they choose. adds optional 'spendNoteHashes' parameter to 'wallet/createTransaction' rpc endpoint to accept a list of hex string note hashes to use to fund a transaction. implements 'fundWithNoteHashes' to load decrypted notes from the specified hashes and use them to build the spends for a raw transaction. ensures that the specified notes have enough value to fund the transaction. requires that notes have an index so that a witness can be created. does not check whether the notes have already been spent. this is intentional to support use cases like 'canceling' or 'speeding up' a transaction by creating a transaction that spends the same notes but offers a higher fee. adds unit tests for wallet and rpc transaction creation methods. * adds error messages to assertions in 'fundFromNoteHashes' * supports partial funding from notes passed to createTransaction if the notes passed to createTransaction do not cover the amount needed, the wallet will fund the rest of the transaction using available unspent notes. refactors fund to optionally take a list of notes. renames 'createSpendsForAsset' to 'addSpendsForAsset' and modifies to take a set of notes already spent and an amount already spent. removes unneeded code: 'checkNoteSpentOnChainAndRepair', 'createSpends' renames 'spendNoteHashes' to 'notes' in RPC and createTransaction method * iterate over amounts needed map entries instead of keys avoid defaulting to 0 if the key is missing. * defaults notes to [] in fund, unindents --- .../createTransaction.test.ts.fixture | 91 ++++ .../routes/wallet/createTransaction.test.ts | 38 ++ .../rpc/routes/wallet/createTransaction.ts | 9 + .../__fixtures__/wallet.test.ts.fixture | 446 +++++++++++++----- ironfish/src/wallet/wallet.test.ts | 224 ++++----- ironfish/src/wallet/wallet.ts | 212 ++++----- 6 files changed, 677 insertions(+), 343 deletions(-) diff --git a/ironfish/src/rpc/routes/wallet/__fixtures__/createTransaction.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/createTransaction.test.ts.fixture index f9386b987e..c0982e1d61 100644 --- a/ironfish/src/rpc/routes/wallet/__fixtures__/createTransaction.test.ts.fixture +++ b/ironfish/src/rpc/routes/wallet/__fixtures__/createTransaction.test.ts.fixture @@ -687,5 +687,96 @@ } ] } + ], + "Route wallet/createTransaction should generate a valid transaction by spending the specified notes": [ + { + "version": 2, + "id": "b09f2e3e-ba86-480e-a503-ce78f1870d32", + "name": "existingAccount", + "spendingKey": "bafc123b3ee9e5a3ff7ad41c20ea0ec778907f9c1fe9287ac799df43a8b296c7", + "viewKey": "cf9b3d304a8febca46713c4628312eb422e44256492b7111bfcad5bbd75e3914729d897e9ca50114b1f771a2420e7379005980e466b4763ab7cb836ec115f646", + "incomingViewKey": "80601f98f2a89b6ee1a75ac52742bea16eedf75d4e3ea7e9d07d853bd3857303", + "outgoingViewKey": "ddf3487844b91e284abf2d324c7bc7a555bd682fa801efd3c1cafb0cf71974c2", + "publicAddress": "81159b88c6958a5b1e294bcc51bb733ff3c126298f909682921ebe22b094586d", + "createdAt": null + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", + "noteCommitment": { + "type": "Buffer", + "data": "base64:v0jKOhi7/kWoAzfjAuvb267KKqnOUDOvSpwM6jxZ0WI=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:t9yKkVISPQPjyG3khNcZX5jWMPx1bzgKTy5o5Po40fU=" + }, + "target": "883423532389192164791648750371459257913741948437809479060803100646309888", + "randomness": "0", + "timestamp": 1682349763914, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA6SYz5mJZGNRglZJN/SxR222hT2XAPr//VByBfr5hqIyuYT9Szj8X5FvjMBZdLifzKIyqlh93NqW/2bRa4AgeWVy8QQANGUM1ghCItNSI2rOYE8tPVefJP1e2BBcjhMLUUi2sHynG8008vSoNqMQu+hPE5Ga59hidpN38My+LCWYZBS7yrqBKt6yLBP/vYDCyhML1xTSw9N5eEOVfJ0+wXt4V6Ru3LXOUT15nFxyeCu2KDpFV+iHyOidX9E7CoQ75z5sjxqfBT7dr4sIvbXVHEhbZjq4B1kcXVqnIb5cv1q2ykbeWPDZGO5cgIU7BxZYSvYARbOpYGmTyecyg/A5/iH5nq2XvlFYhdhbLAi6WcHnkPbY5JrEm9rHoVUcFbUpxqc8rXBssiAOAoAeOaGbB3VjNc1lhBvKjjGn9o/+PTTw3Th1vkkH4dvkWEDwYJ8VgM8Oc6KupGkSVev5/yayDNw2gQ6Rr06MInXsuoYiH7HxlOxbvKnu2eLgW0kfuNqM2QGb20vAJD5KHTsLhCChJF0uGaT0FLmw3JL5MvS35JDwQqa/zFfTGLxw0RLO8gHk9Y+aIigrRliKpWzU37nHuoBXFXoeZz3SWdRGI/VmJ9DlUIDbGVv0TEUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwqkYvxploPJeAdEVjpSEmGdfTW6bmemOKKq2PAEErlOJHmy1PCKYYmYe8pOpxQNolMujZMhkEQC0auebFHUzEDA==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "C4F9242D70C37D1E6063D3B88A4A49ED10C259308159D1F3A5DB7BFFCA27E2CD", + "noteCommitment": { + "type": "Buffer", + "data": "base64:k+sgzHSx9NS9/2YWeH+GvnOnoOf/44S6ijkqFTpkgzo=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:J9nwgBrYHDb5/UMm2CvopP0q5bnqCMOEz5i+fvVpJS4=" + }, + "target": "880842937844725196442695540779332307793253899902937591585455087694081134", + "randomness": "0", + "timestamp": 1682349764614, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAj24sldcyTytI7FloX/QZMef0t4NIqBUvota3BYf6ee20I/rbq9GlGr4iXaV9mF0WFHaobvnYC9kqkOZgxOCa7DrYQ71GEOBb7/s01ZnfauOZMV4UsWa3pZIMq+7ob7/sXA1hOCZe4s5HyCphrpAJ9ndSEcptYdd/rcAoHi3FgQ4XdRl/ob5+sY2w5vZh4n/dhLVWe6euvd45jmBXHgehh+zJmQNJ6Qlc3nDNzkILygaAmQi4U2PvP5Hgdh24Xsyi1jtTt8F3BmNzFG9fdo+aNCg6lIAkMkAGwJ0UWKCnbmaIurE7qxpO9gYiMEWgzjkcvau8DiTftqXUTEZQG1wXm/eX4eeqkvk5PP+jmVoV47SVwcxkrgTzbRitQOSGLt5ZRkChTXGylihlrezmercVksR9cjadtysQCFFs03NNLtgGjBSpvkKHo+BO0v31DN7WUT1iDNoCwREPNa+0VEBOOBzd8ZKsKE1PqKF+COdcw/RTqwgUcBkI+h4a4sHtSA88ags7Dw/SOL2GKKplz3sL3lbinOpHMBvqJZ16mN1LIdF+VcaN5IJEFQ0aTud0j2W38N//uoIDs/UKJjm3D6udD4ZeLNqUUN3iHtx0gVdPRHTZS5m/rEqdn0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwLxvJ42QhCEFC6y3LME7EqMBFrOYOEpAfqH5BL/AYK8F+iQWwtV6uPgdz2kQZeCcpdxuKJk+b67PvMy08Nrx/Bw==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "068264A6E2174F72920376BCC86A684365995FD78393962AACD911D0A185CB35", + "noteCommitment": { + "type": "Buffer", + "data": "base64:iyOJ7SX7LysRDvR4I/rf3E0PgKzJ7/LycBFe/2WIHwA=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:nwkMluSdhPxVwiKuWAGOuSiGxpkU7CNfqEZI3skVJNM=" + }, + "target": "878277375889837647326843029495509009809390053592540685978895509768758568", + "randomness": "0", + "timestamp": 1682349765282, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAArLq5t/l+qHE6E3Ti2WlC0LqJMr7eMiWowoKWH+EOItuN0V8qv87MC459OeBEFM/U/UJhejy7VlqhMuzz2v0GZritdB09K5QqnA8/9iVQaoWz7s5fbpEllPRetMSC3VweBltm0P/I2BTefIV1AiFJLoWU+Ob2UcEm4nG3Xe2aYfYReniGPVkeo5ysyxUJcKUSl8Wtz/MsgeVsEHTUb60URiAtKCRhw4DD/XT4kA91BuqSWrW0sI97K+1ZKtSjR+2OH9q5OTykIKkTmhOyPQTzFT/RsmKwgImaX+6A6qaAVd0jA4bDTN9tqzeBOx41AJt+XVFuTBe4zdbehcypBNVS63/dlqcAdJ0OqtiQYexQ2o2VeR4SHwM5VTwjFsm7oopaEWQAGWACu7a6lXOn1xdMgTN9vKExh4pqPhKov2pTNqBvc1acw6FkpOsVpjCcHBj1fmrriynq+wnDpFlT+AHvbpHvejyWH6gzi80SgP6+iHxu8KgqJ1GsK8OxRqKQzslbPB2PUJn/gD5ZCw9hwdbp5+phaRLk+GCmU6OY7vYK453yJiCTDsRSMUJSFK27n9tEwfXa4GMb7mH1CpaV9zqU28RsUaZqZ4jLs9Q7yMTB/yLQaXyFZbpptUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwxopeq6V3mB0RggjNHGEIGmG0Z5mz05Ykf0lyMV9AQu1moly5ibBoyiBRJtM9Atb8c9GSI7epEE0haiwlSXuQCw==" + } + ] + } ] } \ No newline at end of file diff --git a/ironfish/src/rpc/routes/wallet/createTransaction.test.ts b/ironfish/src/rpc/routes/wallet/createTransaction.test.ts index 3307903d3d..367de076dd 100644 --- a/ironfish/src/rpc/routes/wallet/createTransaction.test.ts +++ b/ironfish/src/rpc/routes/wallet/createTransaction.test.ts @@ -6,6 +6,7 @@ import { Asset } from '@ironfish/rust-nodejs' import { RawTransactionSerde } from '../../../primitives/rawTransaction' import { useAccountFixture, useMinerBlockFixture } from '../../../testUtilities' import { createRouteTest } from '../../../testUtilities/routeTest' +import { AsyncUtils } from '../../../utils' import { ERROR_CODES } from '../../adapters/errors' const REQUEST_PARAMS = { @@ -383,4 +384,41 @@ describe('Route wallet/createTransaction', () => { }), ) }) + + it('should generate a valid transaction by spending the specified notes', async () => { + const sender = await useAccountFixture(routeTest.node.wallet, 'existingAccount') + + for (let i = 0; i < 3; ++i) { + const block = await useMinerBlockFixture( + routeTest.chain, + undefined, + sender, + routeTest.node.wallet, + ) + + await expect(routeTest.node.chain).toAddBlock(block) + + await routeTest.node.wallet.updateHead() + } + + const decryptedNotes = await AsyncUtils.materialize(sender.getNotes()) + const notes = decryptedNotes.map((note) => note.note.hash().toString('hex')) + + const requestParams = { ...REQUEST_PARAMS, notes } + + const response = await routeTest.client.wallet.createTransaction(requestParams) + + expect(response.status).toBe(200) + expect(response.content.transaction).toBeDefined() + + const rawTransactionBytes = Buffer.from(response.content.transaction, 'hex') + const rawTransaction = RawTransactionSerde.deserialize(rawTransactionBytes) + + expect(rawTransaction.outputs.length).toBe(1) + expect(rawTransaction.expiration).toBeDefined() + expect(rawTransaction.burns.length).toBe(0) + expect(rawTransaction.mints.length).toBe(0) + expect(rawTransaction.spends.length).toBe(3) + expect(rawTransaction.fee).toBe(1n) + }) }) diff --git a/ironfish/src/rpc/routes/wallet/createTransaction.ts b/ironfish/src/rpc/routes/wallet/createTransaction.ts index 868e36faae..895fd5b7d4 100644 --- a/ironfish/src/rpc/routes/wallet/createTransaction.ts +++ b/ironfish/src/rpc/routes/wallet/createTransaction.ts @@ -35,6 +35,7 @@ export type CreateTransactionRequest = { expiration?: number expirationDelta?: number confirmations?: number + notes?: string[] } export type CreateTransactionResponse = { @@ -83,6 +84,7 @@ export const CreateTransactionRequestSchema: yup.ObjectSchema { expect(rawTransaction.spends.length).toBe(1) expect(rawTransaction.fee).toBeGreaterThan(0n) }) + + it('should create transaction with a list of note hashes to spend', async () => { + const { node } = nodeTest + + const accountA = await useAccountFixture(node.wallet, 'a') + + 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) + const blockA3 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) + await expect(node.chain).toAddBlock(blockA3) + + await node.wallet.updateHead() + + const notes = [blockA2.minersFee.notes[0].hash()] + + const rawTransaction = await node.wallet.createTransaction({ + account: accountA, + notes, + outputs: [ + { + publicAddress: '0d804ea639b2547d1cd612682bf99f7cad7aad6d59fd5457f61272defcd4bf5b', + amount: 10n, + memo: '', + assetId: Asset.nativeId(), + }, + ], + expiration: 0, + fee: 1n, + }) + + expect(rawTransaction.spends.length).toBe(1) + + const spentNoteHashes = rawTransaction.spends.map((spend) => spend.note.hash()) + expect(spentNoteHashes).toEqual(notes) + }) + + it('should create transaction with a list of multiple note hashes to spend', async () => { + const { node } = nodeTest + + const accountA = await useAccountFixture(node.wallet, 'a') + + 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) + const blockA3 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) + await expect(node.chain).toAddBlock(blockA3) + + await node.wallet.updateHead() + + const notes = [blockA2.minersFee.notes[0].hash(), blockA3.minersFee.notes[0].hash()] + + const rawTransaction = await node.wallet.createTransaction({ + account: accountA, + notes, + outputs: [ + { + publicAddress: '0d804ea639b2547d1cd612682bf99f7cad7aad6d59fd5457f61272defcd4bf5b', + amount: 10n, + memo: '', + assetId: Asset.nativeId(), + }, + ], + expiration: 0, + fee: 1n, + }) + + expect(rawTransaction.spends.length).toBe(2) + + const spentNoteHashes = rawTransaction.spends.map((spend) => spend.note.hash()) + expect(spentNoteHashes).toEqual(notes) + }) + + it('should partially fund a transaction if the note hashes to spend have insufficient funds', async () => { + const { node } = nodeTest + + const accountA = await useAccountFixture(node.wallet, 'a') + + 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) + const blockA3 = await useMinerBlockFixture(node.chain, undefined, accountA, node.wallet) + await expect(node.chain).toAddBlock(blockA3) + + await node.wallet.updateHead() + + const notes = [blockA2.minersFee.notes[0].hash()] + + const rawTransaction = await node.wallet.createTransaction({ + account: accountA, + notes, + outputs: [ + { + publicAddress: '0d804ea639b2547d1cd612682bf99f7cad7aad6d59fd5457f61272defcd4bf5b', + amount: 2000000000n, + memo: '', + assetId: Asset.nativeId(), + }, + ], + expiration: 0, + fee: 1n, + }) + + expect(rawTransaction.spends.length).toBe(2) + + const spentNoteHashes = new BufferSet() + for (const spend of rawTransaction.spends) { + spentNoteHashes.add(spend.note.hash()) + } + + expect(spentNoteHashes.has(notes[0])).toBe(true) + }) }) describe('getTransactionStatus', () => { @@ -1333,111 +1446,6 @@ describe('Accounts', () => { }) }) - describe('createSpendsForAsset', () => { - it('returns spendable notes for a provided asset identifier', async () => { - const { node } = await nodeTest.createSetup() - const account = await useAccountFixture(node.wallet) - - // Get some coins for transaction fees - const blockA = await useMinerBlockFixture(node.chain, 2, account, node.wallet) - await expect(node.chain).toAddBlock(blockA) - await node.wallet.updateHead() - - const asset = new Asset(account.spendingKey, 'mint-asset', 'metadata') - const assetId = asset.id() - const mintValue = BigInt(10) - const mintData = { - name: asset.name().toString('utf8'), - metadata: asset.metadata().toString('utf8'), - value: mintValue, - isNewAsset: true, - } - - // Mint some coins - const blockB = await useBlockFixture(node.chain, async () => { - const raw = await node.wallet.createTransaction({ - account, - mints: [mintData], - fee: 0n, - expiration: 0, - }) - - const transaction = await node.wallet.post({ - transaction: raw, - account, - }) - - return node.chain.newBlock( - [transaction], - await node.strategy.createMinersFee(transaction.fee(), 3, generateKey().spendingKey), - ) - }) - await expect(node.chain).toAddBlock(blockB) - await node.wallet.updateHead() - await expect(node.wallet.getBalance(account, asset.id())).resolves.toMatchObject({ - confirmed: mintValue, - }) - - // transaction should have two notes: one in the custom asset and one in IRON - expect(blockB.transactions[1].notes.length).toBe(2) - - // find the custom asset note to spend - let noteToSpend: Note | null = null - for (const outputNote of blockB.transactions[1].notes) { - const decryptedNote = outputNote.decryptNoteForOwner(account.incomingViewKey) - Assert.isNotUndefined(decryptedNote) - - if (decryptedNote.assetId().equals(assetId)) { - noteToSpend = decryptedNote - break - } - } - Assert.isNotNull(noteToSpend) - - // Check what notes would be spent - const { amount, notes } = await node.wallet.createSpendsForAsset( - account, - assetId, - BigInt(2), - 0, - ) - - expect(amount).toEqual(mintValue) - expect(notes).toHaveLength(1) - expect(notes[0].note).toMatchObject(noteToSpend) - }) - - it('should return spendable notes dependant on confirmations', 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.updateHead() - - const value = BigInt(10) - const assetId = Asset.nativeId() - - const invalidConfirmations = 100 - const validConfirmations = 0 - - const { amount: validAmount, notes: validNotes } = await node.wallet.createSpendsForAsset( - account, - assetId, - value, - validConfirmations, - ) - expect(validAmount).toEqual(2000000000n) - expect(validNotes).toHaveLength(1) - - // No notes should be returned - const { amount: invalidAmount, notes: invalidNotes } = - await node.wallet.createSpendsForAsset(account, assetId, value, invalidConfirmations) - expect(invalidAmount).toEqual(BigInt(0)) - expect(invalidNotes).toHaveLength(0) - }) - }) - describe('addPendingTransaction', () => { it('should not decrypt notes for accounts that have already seen the transaction', async () => { const { node } = await nodeTest.createSetup() diff --git a/ironfish/src/wallet/wallet.ts b/ironfish/src/wallet/wallet.ts index d771ec6ffc..03c080ffa9 100644 --- a/ironfish/src/wallet/wallet.ts +++ b/ironfish/src/wallet/wallet.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/. */ import { Asset, generateKey, Note as NativeNote } from '@ironfish/rust-nodejs' -import { BufferMap } from 'buffer-map' +import { BufferMap, BufferSet } from 'buffer-map' import { v4 as uuid } from 'uuid' import { Assert } from '../assert' import { Blockchain } from '../blockchain' @@ -13,13 +13,13 @@ import { Config } from '../fileStores' import { createRootLogger, Logger } from '../logger' import { MemPool } from '../memPool' import { getFee } from '../memPool/feeEstimator' -import { NoteHasher } from '../merkletree/hasher' -import { NoteWitness, Witness } from '../merkletree/witness' +import { Witness } from '../merkletree/witness' import { Mutex } from '../mutex' import { GENESIS_BLOCK_SEQUENCE } from '../primitives' import { BlockHeader } from '../primitives/blockheader' import { BurnDescription } from '../primitives/burnDescription' import { Note } from '../primitives/note' +import { NoteEncrypted } from '../primitives/noteEncrypted' import { MintData, RawTransaction } from '../primitives/rawTransaction' import { Transaction } from '../primitives/transaction' import { IDatabaseTransaction } from '../storage/database/transaction' @@ -44,8 +44,6 @@ import { HeadValue } from './walletdb/headValue' import { TransactionValue } from './walletdb/transactionValue' import { WalletDB } from './walletdb/walletdb' -const noteHasher = new NoteHasher() - export enum AssetStatus { CONFIRMED = 'confirmed', PENDING = 'pending', @@ -844,6 +842,7 @@ export class Wallet { async createTransaction(options: { account: Account + notes?: Buffer[] outputs?: { publicAddress: string amount: bigint @@ -923,8 +922,8 @@ export class Wallet { } await this.fund(raw, { - fee: raw.fee, account: options.account, + notes: options.notes, confirmations: confirmations, }) @@ -933,8 +932,8 @@ export class Wallet { raw.spends = [] await this.fund(raw, { - fee: raw.fee, account: options.account, + notes: options.notes, confirmations: confirmations, }) } @@ -976,32 +975,82 @@ export class Wallet { async fund( raw: RawTransaction, options: { - fee: bigint account: Account + notes?: Buffer[] confirmations: number }, ): Promise { - const needed = this.buildAmountsNeeded(raw, { - fee: options.fee, - }) + const needed = this.buildAmountsNeeded(raw, { fee: raw.fee }) + const spent = new BufferMap() + const notesSpent = new BufferMap() + + for (const noteHash of options.notes ?? []) { + const decryptedNote = await options.account.getDecryptedNote(noteHash) + Assert.isNotUndefined( + decryptedNote, + `No note found with hash ${noteHash.toString('hex')} for account ${ + options.account.name + }`, + ) + + const witness = await this.getNoteWitness(decryptedNote) + + const assetId = decryptedNote.note.assetId() + + const assetAmountSpent = spent.get(assetId) ?? 0n + spent.set(assetId, assetAmountSpent + decryptedNote.note.value()) + + const assetNotesSpent = notesSpent.get(assetId) ?? new BufferSet() + assetNotesSpent.add(noteHash) + notesSpent.set(assetId, assetNotesSpent) - const spends = await this.createSpends(options.account, needed, options.confirmations) + raw.spends.push({ note: decryptedNote.note, witness }) + } + + for (const [assetId, assetAmountNeeded] of needed.entries()) { + const assetAmountSpent = spent.get(assetId) ?? 0n + const assetNotesSpent = notesSpent.get(assetId) ?? new BufferSet() + + if (assetAmountSpent >= assetAmountNeeded) { + continue + } - for (const spend of spends) { - const witness = new Witness( - spend.witness.treeSize(), - spend.witness.rootHash, - spend.witness.authenticationPath, - noteHasher, + const amountSpent = await this.addSpendsForAsset( + raw, + options.account, + assetId, + assetAmountNeeded, + assetAmountSpent, + assetNotesSpent, + options.confirmations, ) - raw.spends.push({ - note: spend.note, - witness: witness, - }) + if (amountSpent < assetAmountNeeded) { + throw new NotEnoughFundsError(assetId, amountSpent, assetAmountNeeded) + } } } + async getNoteWitness( + note: DecryptedNoteValue, + ): Promise> { + Assert.isNotNull( + note.index, + `Note with hash ${note.note + .hash() + .toString('hex')} is missing an index and cannot be spent.`, + ) + + const witness = await this.chain.notes.witness(note.index) + + Assert.isNotNull( + witness, + `Could not create a witness for note with hash ${note.note.hash().toString('hex')}`, + ) + + return witness + } + private buildAmountsNeeded( raw: RawTransaction, options: { @@ -1024,125 +1073,34 @@ export class Wallet { return amountsNeeded } - private async createSpends( - sender: Account, - amountsNeeded: BufferMap, - confirmations: number, - ): Promise> { - const notesToSpend: Array<{ note: Note; witness: NoteWitness }> = [] - - for (const [assetId, amountNeeded] of amountsNeeded.entries()) { - const { amount, notes } = await this.createSpendsForAsset( - sender, - assetId, - amountNeeded, - confirmations, - ) - - if (amount < amountNeeded) { - throw new NotEnoughFundsError(assetId, amount, amountNeeded) - } - - notesToSpend.push(...notes) - } - - return notesToSpend - } - - async createSpendsForAsset( + async addSpendsForAsset( + raw: RawTransaction, sender: Account, assetId: Buffer, amountNeeded: bigint, + amountSpent: bigint, + notesSpent: BufferSet, confirmations: number, - ): Promise<{ amount: bigint; notes: Array<{ note: Note; witness: NoteWitness }> }> { - let amount = 0n - const notes: Array<{ note: Note; witness: NoteWitness }> = [] - - const head = await sender.getHead() - if (!head) { - return { amount, notes } - } - - for await (const unspentNote of this.getUnspentNotes(sender, assetId, { confirmations })) { - if (unspentNote.note.value() <= 0n) { - continue - } - - Assert.isNotNull(unspentNote.index) - Assert.isNotNull(unspentNote.nullifier) - Assert.isNotNull(unspentNote.sequence) - - if (await this.checkNoteOnChainAndRepair(sender, unspentNote)) { + ): Promise { + for await (const unspentNote of sender.getUnspentNotes(assetId, { + confirmations, + })) { + if (notesSpent.has(unspentNote.note.hash())) { continue } - // Try creating a witness from the note - const witness = await this.chain.notes.witness(unspentNote.index) + const witness = await this.getNoteWitness(unspentNote) - if (witness === null) { - this.logger.debug(`Could not create a witness for note with index ${unspentNote.index}`) - continue - } - - this.logger.debug( - `Accounts: spending note ${unspentNote.index} ${unspentNote.note - .hash() - .toString('hex')} ${unspentNote.note.value()}`, - ) + amountSpent += unspentNote.note.value() - // Otherwise, push the note into the list of notes to spend - notes.push({ note: unspentNote.note, witness }) - amount += unspentNote.note.value() + raw.spends.push({ note: unspentNote.note, witness }) - if (amount >= amountNeeded) { + if (amountSpent >= amountNeeded) { break } } - return { amount, notes } - } - - /** - * Checks if a note is already on the chain when trying to spend it - * - * This function should be deleted once the wallet is detached from the chain, - * either way. It shouldn't be necessary. It's just a hold over function to - * sanity check from wallet 1.0. - * - * @returns true if the note is on the chain already - */ - private async checkNoteOnChainAndRepair( - sender: Account, - unspentNote: DecryptedNoteValue, - ): Promise { - if (!unspentNote.nullifier) { - return false - } - - const spent = await this.chain.nullifiers.contains(unspentNote.nullifier) - - if (!spent) { - return false - } - - this.logger.debug( - `Note was marked unspent, but nullifier found in tree: ${unspentNote.nullifier.toString( - 'hex', - )}`, - ) - - // Update our map so this doesn't happen again - const noteMapValue = await sender.getDecryptedNote(unspentNote.note.hash()) - - if (noteMapValue) { - this.logger.debug(`Unspent note has index ${String(noteMapValue.index)}`) - await this.walletDb.saveDecryptedNote(sender, unspentNote.note.hash(), { - ...noteMapValue, - spent: true, - }) - } - - return true + return amountSpent } broadcastTransaction(transaction: Transaction): void { From 5927154d51104183f2c8dd76ac26d7cf9e3d1c25 Mon Sep 17 00:00:00 2001 From: Daniel Cogan Date: Mon, 24 Apr 2023 20:51:17 -0700 Subject: [PATCH 09/19] Refactor chain/getTransaction (#3841) --- ironfish/src/rpc/adapters/errors.ts | 10 ++ .../getTransaction.test.ts.fixture | 54 +++++++ .../rpc/routes/chain/getTransaction.test.ts | 22 +++ .../src/rpc/routes/chain/getTransaction.ts | 142 +++++++----------- ironfish/src/rpc/routes/chain/index.ts | 1 + ironfish/src/rpc/routes/chain/types.ts | 17 +++ ironfish/src/rpc/routes/wallet/types.ts | 10 ++ 7 files changed, 169 insertions(+), 87 deletions(-) create mode 100644 ironfish/src/rpc/routes/chain/types.ts diff --git a/ironfish/src/rpc/adapters/errors.ts b/ironfish/src/rpc/adapters/errors.ts index f09c71212f..fbbc2c45e9 100644 --- a/ironfish/src/rpc/adapters/errors.ts +++ b/ironfish/src/rpc/adapters/errors.ts @@ -10,6 +10,7 @@ export enum ERROR_CODES { VALIDATION = 'validation', INSUFFICIENT_BALANCE = 'insufficient-balance', UNAUTHENTICATED = 'unauthenticated', + NOT_FOUND = 'not-found', } /** @@ -51,3 +52,12 @@ export class ValidationError extends ResponseError { super(message, code, status) } } + +/** + * A convenience error to throw inside of routes when a resource is not found + */ +export class NotFoundError extends ResponseError { + constructor(message: string, status = 404, code = ERROR_CODES.NOT_FOUND) { + super(message, code, status) + } +} diff --git a/ironfish/src/rpc/routes/chain/__fixtures__/getTransaction.test.ts.fixture b/ironfish/src/rpc/routes/chain/__fixtures__/getTransaction.test.ts.fixture index 76e100dc45..5f0169cd2a 100644 --- a/ironfish/src/rpc/routes/chain/__fixtures__/getTransaction.test.ts.fixture +++ b/ironfish/src/rpc/routes/chain/__fixtures__/getTransaction.test.ts.fixture @@ -54,5 +54,59 @@ } ] } + ], + "Route chain/getTransaction throws an error if the transaction is not found on the block": [ + { + "header": { + "sequence": 2, + "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", + "noteCommitment": { + "type": "Buffer", + "data": "base64:OruYTlQJaQEkG4FN9ff9MWhl7r0SWWOCmM0fI/JfSTU=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:tJkiTil74HGQ//8tn5/mB9aLBo6OV8ndd62mu/AEUtg=" + }, + "target": "883423532389192164791648750371459257913741948437809479060803100646309888", + "randomness": "0", + "timestamp": 1682384908992, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA5bxpmVyfWsL4Go36kVEUbwu+R/BQz8HE8q93KL/2P+WX1f8M7yJitsFMWqK7Qe2MH9/fFckcj+azYcHiulTjSGTOGLQB2xQcC2LO8GnOxaeTuN6Svwyd2LgPc6fZ7sg+jXFb5+cBFzMGGZvGTNtL0DO37YlNPrKKMu/Bn0emJfYOjwk3hmoBs/AHo97TDCszNfdEBzuj6rZO97SHLAFeHXXsoHFCli98RrW71LbxIdWXyP7KR/19lA/gpEYkmQ8wDW8aiuSIdJFsXkRS5g6l/j1VitpiVVJV7RUb58znBdstyiBTa0FQTdqz6oLoyinmIpTArr1dOh/IbywKrWtsTtOjmVBRDuwH8d+LbhbslME2lfKvKIDytNZwu8iZQSpfRUpZ7VStjmzwD/GS+R6ep/xZErYTiDdSCWEYn1A+nmGFaZVzBocqN49tlgQX6QoSSEvnsCr2hGCKhUzPXGc8x2ZyPL7Yv4hFqcIkuauKtkkqIoqSmxfN9nuNTAYv/vEu7Sbxp1e3sC5CS6OzbY5UeQIfiNsvCE3N8LoByYhJ8yEgrHP0o6+Ddpjucji9Zv2yR8/wdefIaLSrAuA2403kFo+E/LHW34tZaa+et0MKsqrbE7cC6oj4/klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwR5+9rquAcqtFw3H6FfUbTCNyIugTZGLZwImtl41oJ1+1EsxxdoBok5y3pgIRwHL+0wjylj4gYnSEZvRnjCaiBw==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "1546416D82113A4F7E09653FB87D7F0AC4229671AE7B9BC8B55622C49F6D81D3", + "noteCommitment": { + "type": "Buffer", + "data": "base64:uCx1AI0d8P672ggEWmwdnMyb4hGyGhCQNyke2s0HhSA=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:fFMZtxtNweTsma/kBz0WtUBcRpBvf/MFlMd4F0yHJr4=" + }, + "target": "880842937844725196442695540779332307793253899902937591585455087694081134", + "randomness": "0", + "timestamp": 1682384909381, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAZIV8ZOL3JTGJnjdZL7Juot17Vui/4o8Z4mAb+ayhijuVyIvbJDivsZpX0IZDkOp9KhOMTNDhHVWNaZUXUim5TwBpXZmdFhsVwK1hBIqdmH2nMVZet13XhTifp5Uu11nyBOGArtlUD7QNfelFMwjgH7naEvuQPNXiWMMnPeVRAKIY9lrRc1aYKt1U7teIHKe483XJYjZfC5UAiPuMnAwrINvsFBOW6lCJhuCFpWeoC/aQ61QoTq0Sra9938nfgaSF+e1uRC/u/sDP2p5kwGTFGeRPN4ls0btk6EjaToskLNcvsLTZLezBjYstLr61QJWfKexSd1Qe0o5Vc8AgBK73t3l/voSBe0dv2ydRwPviK+T6K8iM08dlL95d00tXXL8gvADAn/kVZuhKsfOy/7t2XRU43kpzTr6V4885nYEDupUFhcV85+x9sEJGHipcInkClJ12zDJ+dQoX3wpmmkFgRolZs8bMKk7jtTOxHOQFxEWuhm71TLIi85p97dtu9D+hnK0B8z3oAYcBr3G1FrumTgkhZB44AF6B9oNLlHsVW9r9vAXCH+2Q79OkegPOdH4iWrzqDIvtxgQtSmjUkAJ0Int5QvHY3r2qxQq8GGaSCB3HJiQfC4xmaklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw00Rdsgv2sq5sVDfadsZpkHAccnUZcUOmJmwVgU4BD5X8Da3IpMFsvkxTPy/0x8Wfg/F+puhJL/OoBA74hvQbAA==" + } + ] + } ] } \ No newline at end of file diff --git a/ironfish/src/rpc/routes/chain/getTransaction.test.ts b/ironfish/src/rpc/routes/chain/getTransaction.test.ts index eebf7055f2..83bbb2ecf2 100644 --- a/ironfish/src/rpc/routes/chain/getTransaction.test.ts +++ b/ironfish/src/rpc/routes/chain/getTransaction.test.ts @@ -102,6 +102,28 @@ describe('Route chain/getTransaction', () => { }) }) + it('throws an error if the transaction is not found on the block', async () => { + const { chain } = routeTest + + const block2 = await useMinerBlockFixture(chain) + await expect(chain).toAddBlock(block2) + + const block3 = await useMinerBlockFixture(chain) + await expect(chain).toAddBlock(block3) + + const transaction = block2.transactions[0] + + await expect( + async () => + await routeTest.client + .request('chain/getTransaction', { + transactionHash: transaction.hash().toString('hex'), + blockHash: block3.header.hash.toString('hex'), + }) + .waitForEnd(), + ).rejects.toThrow(RpcRequestError) + }) + it('throws an error if no transaction hash is provided', async () => { await expect( async () => diff --git a/ironfish/src/rpc/routes/chain/getTransaction.ts b/ironfish/src/rpc/routes/chain/getTransaction.ts index eac2850323..e50c6bf2cc 100644 --- a/ironfish/src/rpc/routes/chain/getTransaction.ts +++ b/ironfish/src/rpc/routes/chain/getTransaction.ts @@ -4,9 +4,10 @@ import * as yup from 'yup' import { BlockHashSerdeInstance } from '../../../serde' import { CurrencyUtils } from '../../../utils' -import { ValidationError } from '../../adapters' +import { NotFoundError, ValidationError } from '../../adapters' import { ApiNamespace, router } from '../router' -import { RpcSpend } from '../wallet/types' +import { RpcSpend, RpcSpendSchema } from '../wallet/types' +import { RpcNote, RpcNoteSchema } from './types' export type GetTransactionRequest = { transactionHash: string; blockHash?: string } @@ -18,10 +19,7 @@ export type GetTransactionResponse = { spendsCount: number signature: string spends: RpcSpend[] - notes: { - hash: string - serialized: string - }[] + notes: RpcNote[] mints: { assetId: string value: string @@ -30,9 +28,11 @@ export type GetTransactionResponse = { assetId: string value: string }[] + blockHash: string // Deprecated: use `notes` instead notesEncrypted: string[] } + export const GetTransactionRequestSchema: yup.ObjectSchema = yup .object({ transactionHash: yup.string().defined(), @@ -49,27 +49,8 @@ export const GetTransactionResponseSchema: yup.ObjectSchema( throw new ValidationError(`Missing transaction hash`) } - const hashBuffer = request.data.blockHash + const transactionHashBuffer = Buffer.from(request.data.transactionHash, 'hex') + + const blockHashBuffer = request.data.blockHash ? BlockHashSerdeInstance.deserialize(request.data.blockHash) - : await node.chain.getBlockHashByTransactionHash( - Buffer.from(request.data.transactionHash, 'hex'), - ) + : await node.chain.getBlockHashByTransactionHash(transactionHashBuffer) - if (!hashBuffer) { - throw new ValidationError( + if (!blockHashBuffer) { + throw new NotFoundError( `No block hash found for transaction hash ${request.data.transactionHash}`, ) } - const blockHeader = await node.chain.getHeader(hashBuffer) + const blockHeader = await node.chain.getHeader(blockHashBuffer) if (!blockHeader) { - throw new ValidationError(`No block found`) + throw new NotFoundError( + `No block found for block hash ${blockHashBuffer.toString('hex')}`, + ) } - // Empty response used for case that transaction not found - const rawTransaction: GetTransactionResponse = { - fee: '0', - expiration: 0, - noteSize: 0, - notesCount: 0, - spendsCount: 0, - signature: '', - notesEncrypted: [], - spends: [], - notes: [], - mints: [], - burns: [], - } const transactions = await node.chain.getBlockTransactions(blockHeader) - transactions.map(({ transaction, initialNoteIndex }) => { - if (transaction.hash().toString('hex') === request.data.transactionHash) { - const fee = transaction.fee().toString() - const expiration = transaction.expiration() - const signature = transaction.transactionSignature() - const notesEncrypted = [] - - for (const note of transaction.notes) { - notesEncrypted.push(note.serialize().toString('hex')) - } + const foundTransaction = transactions.find(({ transaction }) => + transaction.hash().equals(transactionHashBuffer), + ) - rawTransaction.fee = fee - rawTransaction.expiration = expiration - rawTransaction.noteSize = initialNoteIndex + transaction.notes.length - rawTransaction.notesCount = transaction.notes.length - rawTransaction.spendsCount = transaction.spends.length - rawTransaction.signature = signature.toString('hex') - rawTransaction.notesEncrypted = notesEncrypted - - rawTransaction.spends = transaction.spends.map((spend) => ({ - nullifier: spend.nullifier.toString('hex'), - commitment: spend.commitment.toString('hex'), - size: spend.size, - })) - - rawTransaction.notes = transaction.notes.map((note) => ({ - hash: note.hash().toString('hex'), - serialized: note.serialize().toString('hex'), - })) + if (!foundTransaction) { + throw new NotFoundError( + `Transaction not found on block ${blockHashBuffer.toString('hex')}`, + ) + } - rawTransaction.mints = transaction.mints.map((mint) => ({ - assetId: mint.asset.id().toString('hex'), - value: CurrencyUtils.encode(mint.value), - })) + const { transaction, initialNoteIndex } = foundTransaction - rawTransaction.burns = transaction.burns.map((burn) => ({ - assetId: burn.assetId.toString('hex'), - value: CurrencyUtils.encode(burn.value), - })) - } - }) + const rawTransaction: GetTransactionResponse = { + fee: transaction.fee().toString(), + expiration: transaction.expiration(), + 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) => ({ + hash: note.hash().toString('hex'), + serialized: note.serialize().toString('hex'), + })), + mints: transaction.mints.map((mint) => ({ + assetId: mint.asset.id().toString('hex'), + value: CurrencyUtils.encode(mint.value), + })), + burns: transaction.burns.map((burn) => ({ + assetId: burn.assetId.toString('hex'), + value: CurrencyUtils.encode(burn.value), + })), + spends: transaction.spends.map((spend) => ({ + nullifier: spend.nullifier.toString('hex'), + commitment: spend.commitment.toString('hex'), + size: spend.size, + })), + blockHash: blockHashBuffer.toString('hex'), + } request.end(rawTransaction) }, diff --git a/ironfish/src/rpc/routes/chain/index.ts b/ironfish/src/rpc/routes/chain/index.ts index 01cf2c49a8..bec1b25df1 100644 --- a/ironfish/src/rpc/routes/chain/index.ts +++ b/ironfish/src/rpc/routes/chain/index.ts @@ -18,3 +18,4 @@ export * from './getConsensusParameters' export * from './getAsset' export * from './getNetworkInfo' export * from './getNoteWitness' +export * from './types' diff --git a/ironfish/src/rpc/routes/chain/types.ts b/ironfish/src/rpc/routes/chain/types.ts new file mode 100644 index 0000000000..ff18ce9448 --- /dev/null +++ b/ironfish/src/rpc/routes/chain/types.ts @@ -0,0 +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 * as yup from 'yup' + +export type RpcNote = { + hash: string + serialized: string +} + +export const RpcNoteSchema: yup.ObjectSchema = yup + .object({ + hash: yup.string().defined(), + serialized: yup.string().defined(), + }) + .defined() diff --git a/ironfish/src/rpc/routes/wallet/types.ts b/ironfish/src/rpc/routes/wallet/types.ts index f4fbdde5fc..33bc5c0548 100644 --- a/ironfish/src/rpc/routes/wallet/types.ts +++ b/ironfish/src/rpc/routes/wallet/types.ts @@ -2,6 +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/. */ +import * as yup from 'yup' + export type RpcAccountTransaction = { hash: string fee: string @@ -39,3 +41,11 @@ export type RpcSpend = { commitment: string size: number } + +export const RpcSpendSchema: yup.ObjectSchema = yup + .object({ + nullifier: yup.string().defined(), + commitment: yup.string().defined(), + size: yup.number().defined(), + }) + .defined() From aaf420ba611e150eb898d8b3add19f6780d5b1da Mon Sep 17 00:00:00 2001 From: Rohan Jadvani <5459049+rohanjadvani@users.noreply.github.com> Date: Tue, 25 Apr 2023 01:00:10 -0400 Subject: [PATCH 10/19] feat(ironfish): Return note indices in getNotes (#3812) --- ironfish/src/rpc/routes/wallet/getNotes.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ironfish/src/rpc/routes/wallet/getNotes.ts b/ironfish/src/rpc/routes/wallet/getNotes.ts index 9ef383fe00..b89198042d 100644 --- a/ironfish/src/rpc/routes/wallet/getNotes.ts +++ b/ironfish/src/rpc/routes/wallet/getNotes.ts @@ -16,6 +16,7 @@ export type GetAccountNotesStreamResponse = { sender: string noteHash: string transactionHash: string + index: number | null spent: boolean | undefined } @@ -36,6 +37,7 @@ export const GetAccountNotesStreamResponseSchema: yup.ObjectSchema Date: Tue, 25 Apr 2023 13:36:05 -0400 Subject: [PATCH 11/19] feat(ironfish): add spends to `wallet/getTransactions` (#3834) * feat(ironfish): add spends to wallet/getTransactions * test(ironfish) refactor spend / notes tests for wallet/getTransactions into separate tests --- .../getTransactions.test.ts.fixture | 177 +++++++++++------- .../rpc/routes/wallet/getTransactions.test.ts | 37 ++++ .../src/rpc/routes/wallet/getTransactions.ts | 26 ++- 3 files changed, 174 insertions(+), 66 deletions(-) diff --git a/ironfish/src/rpc/routes/wallet/__fixtures__/getTransactions.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/getTransactions.test.ts.fixture index 79e80688cc..f5aa7ae7c7 100644 --- a/ironfish/src/rpc/routes/wallet/__fixtures__/getTransactions.test.ts.fixture +++ b/ironfish/src/rpc/routes/wallet/__fixtures__/getTransactions.test.ts.fixture @@ -2,13 +2,13 @@ "Route wallet/getAccountTransactions streams the associated transaction for a hash": [ { "version": 2, - "id": "9abbc636-7633-4f12-92a0-db782ca14a00", + "id": "97d34fcb-6ba5-464c-b911-9ab7f69d04ba", "name": "test", - "spendingKey": "91b2b2dc63dd9c563006d3502fd3067c06585ccdb3cd2aa1edd27cd39650f53a", - "viewKey": "0e15d9c17f2fbb4a3ce023304aa25f5b712005bdc84ac4166610067679b7c60e2c4d38d4e0dc20827b667afd0b9db6bfbc28594d3f2a7f803238562e9ac9b54c", - "incomingViewKey": "e7454586ca0b39118f075c7ff1dc408a78e6bb0fceb333440641cb443d61b401", - "outgoingViewKey": "edc549ad5b2bd8ef315c62e685cf7801a518c3befaeddff937720c5770f45749", - "publicAddress": "576ef7bb001a83ca839a89b73cc0d029a1f02b841f03a9d300304704c321e34a", + "spendingKey": "d2b98d37dca9911ac860e17128198a9897e6f8b83edf38472d7293ffe2979bf0", + "viewKey": "c6686393eb4485cea51d881c850d0e9ace0e91e0d6f8057bf12e75b3ea83df49eea4f671fa878b4247ac7fb7502712eb3547f81d9a060ff52843e1c8173f7a4d", + "incomingViewKey": "acf7b1c3237f8133c4250429d101eb77187994d42a54164a61ea88769cbd8201", + "outgoingViewKey": "0efe6b3f030701efd7cd662be76c32c373b49ba0c401a544bf6022e56dd5a6d1", + "publicAddress": "96e4356325d89e966628f6fd9275ed7c5292fe30bb6b6f9059949908c00c85c1", "createdAt": null }, { @@ -17,15 +17,15 @@ "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", "noteCommitment": { "type": "Buffer", - "data": "base64:lCy0WfbvvOjqArU2bJpHpPHOBTc27bZfI9KXGPNFwBM=" + "data": "base64:VrZbEHiKZhZ/N2nB8cL9BB4oHJuMH1pM7/xmKOaPqkA=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:z8sITqsd99wHpnmnadWg31hXlWuzyA7mFetlJi+QCzI=" + "data": "base64:iFtskpdS68uzLF9t0sevzzbWETFznjO9KAs1l2u51Hg=" }, "target": "883423532389192164791648750371459257913741948437809479060803100646309888", "randomness": "0", - "timestamp": 1681340302510, + "timestamp": 1682373466616, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -33,7 +33,7 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAoNgqRhvdM+v+gN59aKtBaOsCd9GfPf4BOxIYkmLM1WG1EVL5EypQkR7rPgSnMIh4riF5FtpUZRS6haX5yRrCbtibhdqqYAHwBE4JmqQELnyTTdGS/JXEhcFWbCTiJH9P1rv7cVMTMzCAxYDyWe33HaVDYk9WOejqGhceWMU0FKEOAuLElS9ZIL22+1ltIoi/4DQXY9QW4RU/QQBf9MMPS3dPK5bxJKUIrfXal64ZcVuwFXjnZpQzHq2FdMZq0kEPze7iVrm+kTs0/q+Vdf6G8ELz+QR3FNfyTkTWkQMMQGhF2pxAQnTN/A9bJkOTmCY5nizoJmrhh6YL+9qIcjtzKDXaktV/0bT47aPpICZY/Jnn9/chpR1N26Jzm9YYXagml56SmBpEU4l9GKVz3QKSNnQwBL53+Ekihc6fdAGfDYocu6muW+5Dywr7sMyOaSaR7qXrzl5YemLQ4Nq9SOdg8NXz/J4UtWafS2aEd/y4XWSndmq10TOZKfeGW1wh/CYZhhW8hY6sgjeC/Jr1NXoCfUL7rD4fOJsqLQ5zJP6klFTMRmvxprswimzMHAWVi9GYiwML/z22uXppe60tp4KYZLS7wlA2SMZneVitwbX/FH4VmtaPkSc+80lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwPFJqo6cp+NZf4qzXNutOrxSUMZ0laAwgH0Tmij/52jOkf2C2nIb36Qe1cgMHQWkkRtmbKZWmtw20Qlsn7kA8CA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAamm8Q7ihiSUg61/X167mg8q6yBx8TkaqN+NIm4i4jbOlWt9nD7RDM0H5RAJmRBgPto4iRmYhSLe3dSW3u7nHc9Bk45ZXI4Kvpove3E2XsoyWLtyXkD4ro29Uguiznu1PPiAH72dCxgWoV3LS7tbbcgf3dKeTTAGSqLhZl9x9+PkZMSM2IKq4hyY9KTZPANLHXMOUbltfpbMGatxUtJcXwEc6pu5vQ4Xfa5YfzzLzdOuoIjD9ole5zD6OUnPPQnjsVYJiy6as6c+iq8MdJSUloW/Sey6neBsFXxMB3hmjhxDwaG31G89qd6jdWAxIrJ/eqoKX86CXY1Dl6aUL/KXqX5vTSFskBJ9thJnM9gIHLBbCUlSFCd49U2IckF/iDXQSy525TNSvxMEdbBqdEBiZ4YM90+VH97gX33Kn1F7r69SwHJilb0Bq6y1+dITr79d9IXJh8RuGsUHpzyEDJgIvoJmTpHtCcOrIVDq7AFwU4PQ87V/5L6KDvDPlPHy/c9BpUcU8V7R0+yLESgssui8sy8oyQ5vkJ69qIzbFtjyfInlx/UfComirpidNhH5PRpFflcmuO2vlhfuCeGIftdNkUVqxXHh0StpJShyYECJdmyd7WfdlK9fj1klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwwFi0HOuZZcGprBhmCjUmjFLBCxm/kdy/GM6YudfKbZUPcbWC0CceFu51KbFU/fFTQeltyS0ilL4vurxD5rFgAw==" } ] } @@ -41,17 +41,17 @@ "Route wallet/getAccountTransactions throws a ValidationError for invalid sequences": [ { "version": 2, - "id": "d82d2930-cef7-45b1-a70f-1621272a2e10", + "id": "c927bc7d-3d17-41f3-8cb6-db74856f40ef", "name": "invalid-sequence", - "spendingKey": "93565b388cefcb5cb10d0c948d1f42f55a61a5ce2ab5a85c43342955f57fe422", - "viewKey": "b6649866be67541d557fba2b5a8fe8af121c4928996334ab90508c01d6b5ba01a51e7f3a30f2afe21b7851e7fb631e5b74d409d6d0ab56765a8ecc9cdc2afd43", - "incomingViewKey": "31f3ac75b3a0388901513b6a66d4e3610ca7866e063f8d762c1f6d453f8efa00", - "outgoingViewKey": "7ba10559777ff04115cecd230d49168d23c66e0883967f080ce40f37b6cf58e6", - "publicAddress": "767ecb0c25374d48fbac222411930f9481846566420e81da5df20e6264a4c9bb", + "spendingKey": "af73ab02cb3556f31d94a0783b536c4b865023086fee55430b9b7449e1aed03a", + "viewKey": "8233ffd5b3e1e9694d3120b31a12f825a6d47084cbaf428ece5540e4bc40d866e29369bf9d14f08cec3c1da28321bb50a072cd549a7a4a7e93c1a6e88247b32c", + "incomingViewKey": "25b73475d54bcda9320bb4d93016c9dbb4a92b6a22f1fec284268f09f2de2102", + "outgoingViewKey": "1c72638f8de10ed5e749718f99b1742c0eb08c352061ab85a4cf5b6ef2d62964", + "publicAddress": "2203b2050769be9ee21762106c26cd55288c0ed9ca96cfb7305f4c1c653e4a00", "createdAt": { "hash": { "type": "Buffer", - "data": "base64:8+xJbmRRG5eKVFDJb1OK3GkwY5+ss4OK3PuvcW+S/Ew=" + "data": "base64:rF8RhzuBJaIDpl1iWgb+C/hVfRci401k/yQye8QhFT4=" }, "sequence": 2 } @@ -60,40 +60,40 @@ "Route wallet/getAccountTransactions streams back transactions for a given block sequence": [ { "version": 2, - "id": "2f5ac628-14bc-40f6-94f4-0bebbfe8b32b", + "id": "d9ae515d-0c52-4051-96c0-37bfbc82f19d", "name": "valid-sequence", - "spendingKey": "20f9321aa00962cf391926dfc78ddf00e09dbdd342e186001e45c4d00eecbde6", - "viewKey": "46c831ac28336fe020a82bbd309551496d453eea041eca8e0276ecfdc83ee0339600cf2bb3d18799fc33378d741e162f7f9b9ec1110ebf9507d5263c8b8f1505", - "incomingViewKey": "0946d77b13f10bca7a9f893cfd7526327ba5831c8f07ca089ea6028335ed7f06", - "outgoingViewKey": "f12add34a4c589006d5baa42dd08be7dec41b211ad2ac16df4d1252defba6ec9", - "publicAddress": "97948359e2da1fa21f7284b1355b6960fb1d981ae4c5b041fba34b80dbc81f0b", + "spendingKey": "11386b85923e629af57a25c305fc1cc3730360ac0d63a71eae76b3974e3efa34", + "viewKey": "30210190bd76920390a4138f7093595627c5a3824aecf613bcf94caa95503df1b65eeee445211f268674040ee7e974f83c09dc295e73241fc8070bbdd0a5401f", + "incomingViewKey": "4018790be055256af864cd65e2e3c7f6095686b855d007b9bbe1cae804f95501", + "outgoingViewKey": "3bb910120482cc7fafc428a3c92acdd37bc6f43dfdd5d4144986c4b825df4e91", + "publicAddress": "fc8c7d8c9d5af3c9de5352ad8c089ecb355b956255d684cff23bb7a4d30bcbd2", "createdAt": { "hash": { "type": "Buffer", - "data": "base64:8+xJbmRRG5eKVFDJb1OK3GkwY5+ss4OK3PuvcW+S/Ew=" + "data": "base64:rF8RhzuBJaIDpl1iWgb+C/hVfRci401k/yQye8QhFT4=" }, "sequence": 2 } }, { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1KarVct5+D+KAyme+sVx+49tu/lhmI75rI+vkSWOk2YvZKp2Fu0GoK4mtx+u2mJc0aD2JwL76c5yP1VgrzppCnWgDDpmuO9CQ++U+uu3lGtcS/90/95pbGBEAECtCOL18VJfpuoWVlBSmTWq60ztt8fmq+k5YyhiSp8fpdkbigYBnq/MOyKYGFqwxwJPJRk6KZ5jX2Jpc3gD00S7mKXET68wK3eGGMWV2gEh94IgCGSMFkd7lx+n+jABzBYYV8l8kowlQbp0eO8SQSxZP/RpUEcF2igmrMnFw8aJEVxnLiBHDQMLtRF7dNbevWxWat97J4GHyfZ1U2MHrLoOhSjP0y09uw03/yBjyXWtBMos6boqh5u3k9efvJ1SfELHyY5j6LR9l0ZvDtnSutjlX3vUjNvhnYKZB4vPz4WW0yMDlmHjQgCnUS6qBkQM+vZH97dplGPe+S1r3tU7ML9e5rsQm4sJ4cAlA3X5KXCqudmlboWRILxszmFvCYUlPwsWahWZgiafXQQpKJXZM1ePgD7Kcys8oIGk5j3jPmnH7kko2artHFuQhnBGV90MecbPhgUabXwguPnTyJ0TJVfeEWEwuMfXcT3RG1ABgbjndfFwuu8C1V02SzQDwuhxJytYJtBVxdga3zby/LypULBR2vvkh94rqSz2yoG43svZLo1xbMkM2XYQTJQ8pdbaNPlDkt+xMp/7iGTRBHVr1fIcM5po1zgzd1G+Y9loIC5wL8ZompRU5/Hu6olakCVyT7Uqn2Zt7r5QZl0yxBmMAfFzOA6V+FefgAqDbPQrQYgvimHG7E8NTWQqUqA7+Q5YS9mRdRHtv0ydd/03Pyj6N5NFBGjycJUAUTFR4zMBt6aotNf+GfSvk0x6WWWmi+amHiBzyEAhc1/za3x6ywx4pv92lEKQs/c9zozvxfgizstKhfSb7CC/INSYheSIsff57O+tjbqO8v1S1JvQwqO9qOwR6hI0RYDGom7rhcul5SDWeLaH6IfcoSxNVtpYPsdmBrkxbBB+6NLgNvIHwthc3NldAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAJ9kYbudjXwx98UuwzwHW+wc3mxeyxovrucEDchON2gLa3jN2Zn9PXGumex62BhLdFU6WA9ZTHFlqpz4bgsR+wERsABlqyZInusKZcEvxcKDxxphT43c8IHw9WeThHX98Q2V9hDAz1XyqLzrAD16IdM/AqA0y9ICMXkgvz/Sg+YH" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6taQuS9La7aqLZDmp3GGL9llFQpLQquM0lijoCeMsj6tOYR2H7FlO+cldv1xUYJaWEER497E0wbI1F5lhqyy8gCTglB7zouWQo43FxP9e6mQxnK8PVLetzlUvL1Q2jfaXY01yVM1Quce6VgAbGLJ2/4q3ea47ea9mYrsWat38dcIiHzaUxR9WycWEAyG/cYlm0amwW1vHQP8byxNMKaisXjHP8R0UY8jtnJVysaykhSyvhg3zGPywSKJJxCrO/C6qQrx4DsiuAX+qMTyhb3ReMSCD6p6wIjo7u0jfL8m9iLDGWhPIrYg2BcstUiLAgT+sEMvcdnk9loJMBdIUf6SWkJNF9uYlJ+M/R8p4FeBX8nqvn8AnJMhZLD3xbsZ1kRSqXGVqdSEu8mYYtfWweFBcmtB6uk9HnrPw2lbXqvZ1Z96ihBd51S5EyD0sf0pqKGMh1C2MIaOh2wwOm1ZsnNYVNXYF1PXpY3bVKCJYc2XTmxEO+WNz0orDjth+3eHELV+Mkka0KukX03u3JIM7ITKPw2qNxO3bm7AfHJ8SRhVbaMVdC8y1Eg/ZsQuYSN+y0PNgInh44mLv2t3xwXBzN5SrPsfHB0quJ1NYWzOIrbC/gtHPkqyrhUkdcpBwh2kYdUeaubtHDTLh5PMo6RCv8Kre/unYAynnqK0weK+az6AQSuXb8ME77+qezCzCt8av1AUKCtjqn5SfywoAXdglg5+nuadrrehNORjl8tT2gJmkqfnnEEM/O3HCl7PNaPgJNvsyqIHdiS4vTN8ncIzby7LO8ZyzXZYSBsugVShMP/dV+dWXjlVZtch5mtW7PmYgM5UDcvKY0ZRfAcRsR15VdWN7Jxn7awiiCSvATrKgmt0kUBPMvRF0IN4BRBZBu8x50nSpszMmHWW/ujnyCYBjjHb6/2UIa/CkiRmqKiXb2ET3L7psAlV+9K9Kr/VzULzmmeJIwWKgqvY2llqOxEXBD+pBXIJhnfS+LxT/Ix9jJ1a88neU1KtjAieyzVblWJV1oTP8ju3pNMLy9Jhc3NldAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAALQp2cDiGrw1yi+UVivnyWhnVcQmr8+qs8UfWVVTniitxfpnBqUqXCYVdDM/zAdWIGrLKtnEAbTd4OrVefSHPA6FEGE0R89Nf3i89qwINls6kCzcbKcpqMuD/SGzpdl5lQ0x0OOA9O8no4UM+SrAh/no8hQj0+/wQHbGiYMLJv8L" }, { "header": { "sequence": 3, - "previousBlockHash": "F3EC496E64511B978A5450C96F538ADC6930639FACB3838ADCFBAF716F92FC4C", + "previousBlockHash": "AC5F11873B8125A203A65D625A06FE0BF8557D1722E34D64FF24327BC421153E", "noteCommitment": { "type": "Buffer", - "data": "base64:auK1qpgrRNXOEsIbkT37cCSbZLaOJT8CA01gVY/uGAk=" + "data": "base64:ueFAA7qXhhzyN0lOBHYPHAekYhdhecJD6Ni6ARG/5Go=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:NAke0SckGq5AGFyyf9WLmKtOWtB9jwHBmWXpEWsvxuM=" + "data": "base64:mHkrOw5xSGIL0zsjJQhJzZE/W7iMtPGkwpY3PgT3Dlc=" }, - "target": "881271989446208257911980828427057262643615932976441214377264856368067535", + "target": "880842937844725196442695540779332307793253899902937591585455087694081134", "randomness": "0", - "timestamp": 1681340309006, + "timestamp": 1682373467491, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 6, "work": "0" @@ -101,11 +101,11 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAUamz/La8HjUliETgc3uGc0AfjsS3uVV7F4DW/kZP5ayOJd0pTXMl34n6KxuDHHWJUprnGM6M4o9eClVL3MTdmfvCjbO6WY9tDkVDltJ4BXGSILtq7+mTbkSizjibGUaEpYXlGk7Vcc9KuECeQBjJ1K/b1g6usY5Y+KXbm3tblAsP7PbbTxuwwl82rhYKXMji23RGfOm3jSZrkrGJ0tcQMt7HxMvvz1jjfLW9VgolAfKoiLcOgj5ewtMJVEDkN4BOcokZUfztndy3mYJpQ0uo3qT+pLmIeAXCuKNl/jSOOwGbCwcmoAlmIZPVGYEoJRhY5RhzDAkDcIprjtrN7+3E5xG9m9V7p/6Gx3pG0t+j66KmU2Uml6AsIFIMacNT9SMS9hB6RYyHVlIWrUdZF1W4lHNzVTXUQfU/62zjMSZMWbq9TYhpD3I3wxf2wH/0svUwuE2goYYLkMr/nEOEP46kNq5ahlV9S+JHFWP052MB87lowQ7gX1/bZYGGjpDbAybwfZXw8eanBBURMVxtc/W6orWmqQh2nfwbIeYcJ4qPlLyhtZxk6or11NnCkJShQH8nHraReG4e0wuzAKE3ExKgsGYAlh3ZO90DI3BCbtVHfOcV8eWaXioBh0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwuakMNCwRjPB1q56pDZLYZfMoxZEqX6zmP7SgOGZYNmyUj8vyIVflIj4Ui2ZSSeUORA5t2k+1JU7k9jwZYV1mDg==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAhasAOtdhry261gNVAVOKukGkUGQFN+mGTdDNGt2aTDKx5ZIrIH6TMimy4QE5vy9TUgfm8o1l20gzYPE1R/JLr24sU2+xWy087jewHqmh0BuQsqFcQq8UJcsqC2fbRIZIKutl+RUqrI/oAiYEOfOmp9nD8NQdxjQ/SaMWKOLwrfYE+kebPknFHMdnZh3/EhlowT7oBlt3oJsU7JSmKGfcRzmqeYXi2miDskwxzJZhk9azGFuSF+UfWZq0lJZbqkftV7OJnfvhQtLL4enF4O8WHwnna+D5zgdKOqjlBk4S0R055dtOTPvlLWo5rVImd9SlUw3MJpiIYFc72SofAT4Cb0rmoxyWzjJDDXdqeLN+FTzNFOeKUyoZw97NwsoA2v81rSG2l5yqABuyUwgwdSTblzCuSX8YZOlisfjMrghO6mAJ1fOpuehKTK0+PaBPblaXPiyo0mWTN7sM7D4nJMeOV698r9XZu1D6aGtCy1MmCiUt/Y5O+u9buMgKKIpAaRq5vOkIoEQ5jo4661uUL36hC9+X7v87hHOHpgAghWBZO3Q1x+RYNYjW5ZI+TRC6AqK23V2QYrsllydr5EUAvOwrvkYlTyBitModlckKI/GakaXUQgokkh1UCklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAww8mIlwcK7EPch0cFEy7KtEOB9OgS9a9KjaZ3trceRGb1vunkvHMhaZ6kb1vIZaEadalHdgSI6HsUPfVpIXGBBQ==" }, { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf1KarVct5+D+KAyme+sVx+49tu/lhmI75rI+vkSWOk2YvZKp2Fu0GoK4mtx+u2mJc0aD2JwL76c5yP1VgrzppCnWgDDpmuO9CQ++U+uu3lGtcS/90/95pbGBEAECtCOL18VJfpuoWVlBSmTWq60ztt8fmq+k5YyhiSp8fpdkbigYBnq/MOyKYGFqwxwJPJRk6KZ5jX2Jpc3gD00S7mKXET68wK3eGGMWV2gEh94IgCGSMFkd7lx+n+jABzBYYV8l8kowlQbp0eO8SQSxZP/RpUEcF2igmrMnFw8aJEVxnLiBHDQMLtRF7dNbevWxWat97J4GHyfZ1U2MHrLoOhSjP0y09uw03/yBjyXWtBMos6boqh5u3k9efvJ1SfELHyY5j6LR9l0ZvDtnSutjlX3vUjNvhnYKZB4vPz4WW0yMDlmHjQgCnUS6qBkQM+vZH97dplGPe+S1r3tU7ML9e5rsQm4sJ4cAlA3X5KXCqudmlboWRILxszmFvCYUlPwsWahWZgiafXQQpKJXZM1ePgD7Kcys8oIGk5j3jPmnH7kko2artHFuQhnBGV90MecbPhgUabXwguPnTyJ0TJVfeEWEwuMfXcT3RG1ABgbjndfFwuu8C1V02SzQDwuhxJytYJtBVxdga3zby/LypULBR2vvkh94rqSz2yoG43svZLo1xbMkM2XYQTJQ8pdbaNPlDkt+xMp/7iGTRBHVr1fIcM5po1zgzd1G+Y9loIC5wL8ZompRU5/Hu6olakCVyT7Uqn2Zt7r5QZl0yxBmMAfFzOA6V+FefgAqDbPQrQYgvimHG7E8NTWQqUqA7+Q5YS9mRdRHtv0ydd/03Pyj6N5NFBGjycJUAUTFR4zMBt6aotNf+GfSvk0x6WWWmi+amHiBzyEAhc1/za3x6ywx4pv92lEKQs/c9zozvxfgizstKhfSb7CC/INSYheSIsff57O+tjbqO8v1S1JvQwqO9qOwR6hI0RYDGom7rhcul5SDWeLaH6IfcoSxNVtpYPsdmBrkxbBB+6NLgNvIHwthc3NldAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAJ9kYbudjXwx98UuwzwHW+wc3mxeyxovrucEDchON2gLa3jN2Zn9PXGumex62BhLdFU6WA9ZTHFlqpz4bgsR+wERsABlqyZInusKZcEvxcKDxxphT43c8IHw9WeThHX98Q2V9hDAz1XyqLzrAD16IdM/AqA0y9ICMXkgvz/Sg+YH" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6taQuS9La7aqLZDmp3GGL9llFQpLQquM0lijoCeMsj6tOYR2H7FlO+cldv1xUYJaWEER497E0wbI1F5lhqyy8gCTglB7zouWQo43FxP9e6mQxnK8PVLetzlUvL1Q2jfaXY01yVM1Quce6VgAbGLJ2/4q3ea47ea9mYrsWat38dcIiHzaUxR9WycWEAyG/cYlm0amwW1vHQP8byxNMKaisXjHP8R0UY8jtnJVysaykhSyvhg3zGPywSKJJxCrO/C6qQrx4DsiuAX+qMTyhb3ReMSCD6p6wIjo7u0jfL8m9iLDGWhPIrYg2BcstUiLAgT+sEMvcdnk9loJMBdIUf6SWkJNF9uYlJ+M/R8p4FeBX8nqvn8AnJMhZLD3xbsZ1kRSqXGVqdSEu8mYYtfWweFBcmtB6uk9HnrPw2lbXqvZ1Z96ihBd51S5EyD0sf0pqKGMh1C2MIaOh2wwOm1ZsnNYVNXYF1PXpY3bVKCJYc2XTmxEO+WNz0orDjth+3eHELV+Mkka0KukX03u3JIM7ITKPw2qNxO3bm7AfHJ8SRhVbaMVdC8y1Eg/ZsQuYSN+y0PNgInh44mLv2t3xwXBzN5SrPsfHB0quJ1NYWzOIrbC/gtHPkqyrhUkdcpBwh2kYdUeaubtHDTLh5PMo6RCv8Kre/unYAynnqK0weK+az6AQSuXb8ME77+qezCzCt8av1AUKCtjqn5SfywoAXdglg5+nuadrrehNORjl8tT2gJmkqfnnEEM/O3HCl7PNaPgJNvsyqIHdiS4vTN8ncIzby7LO8ZyzXZYSBsugVShMP/dV+dWXjlVZtch5mtW7PmYgM5UDcvKY0ZRfAcRsR15VdWN7Jxn7awiiCSvATrKgmt0kUBPMvRF0IN4BRBZBu8x50nSpszMmHWW/ujnyCYBjjHb6/2UIa/CkiRmqKiXb2ET3L7psAlV+9K9Kr/VzULzmmeJIwWKgqvY2llqOxEXBD+pBXIJhnfS+LxT/Ix9jJ1a88neU1KtjAieyzVblWJV1oTP8ju3pNMLy9Jhc3NldAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1ldGFkYXRhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAALQp2cDiGrw1yi+UVivnyWhnVcQmr8+qs8UfWVVTniitxfpnBqUqXCYVdDM/zAdWIGrLKtnEAbTd4OrVefSHPA6FEGE0R89Nf3i89qwINls6kCzcbKcpqMuD/SGzpdl5lQ0x0OOA9O8no4UM+SrAh/no8hQj0+/wQHbGiYMLJv8L" } ] } @@ -113,17 +113,17 @@ "Route wallet/getAccountTransactions streams back all transactions by default": [ { "version": 2, - "id": "7835dac4-6956-4084-b83b-f14fc950c1b1", + "id": "997f3cb2-dd15-4a12-ac36-aec97c35d4a9", "name": "default-stream", - "spendingKey": "d537d977c50468f574e7a0fc0fe8895ed1fb1d22baec83a7e24e934ea75294c2", - "viewKey": "1d75cf5c1e60ba2ee43a7bb5edffc3fa52d0e4ec4c57a997f057301470521ec70fad9da80b29d5a581035c5ad49152f7d3969d33aee7b52ccb7077545e1c9cef", - "incomingViewKey": "2d85faa273c4937ecfac7c19e175f9a61a8eb8e5b32e6335a145949b4f0da704", - "outgoingViewKey": "68f6cb905f2c4c5a16b9566ff4a1c53d2c17ba2c91b4a8688502adc825cab444", - "publicAddress": "0a1679099ec7f9dcb45b273b8b2cf0a6f01c91d8a47eed973ebd8e795dd2c4c3", + "spendingKey": "3e02bdf31572777c5ad0265d111fe4741c4d4519dc57363b5308149bd3afc4c2", + "viewKey": "05c91b5fb519a42ee6397c1194ee55aedf254bfe54a8752acc44160aa4182728c05a2c52b95117e50322a93e7bef083fbf4075a9166000c845b47f4069c290d4", + "incomingViewKey": "377db54ded668bb8be752501ee08f8b17988a608368f5e85f095fc13c1bba003", + "outgoingViewKey": "4276838d06bc7229b48e0084f376329911d4e15dcd28482b68341ef35b2f4ae2", + "publicAddress": "3e0bdf39c28a7ddc9450862d06c54098c3a839e79e49890a6a28a53d13801973", "createdAt": { "hash": { "type": "Buffer", - "data": "base64:onBosh37HEJo5A/C1lysl1Oc5FLajnX8ljtH+2VRbLs=" + "data": "base64:AUcan7CNP85S8zIbEcFbjDrRK2eDVu7Aje1EXXv/6Co=" }, "sequence": 3 } @@ -131,18 +131,18 @@ { "header": { "sequence": 4, - "previousBlockHash": "A27068B21DFB1C4268E40FC2D65CAC97539CE452DA8E75FC963B47FB65516CBB", + "previousBlockHash": "01471A9FB08D3FCE52F3321B11C15B8C3AD12B678356EEC08DED445D7BFFE82A", "noteCommitment": { "type": "Buffer", - "data": "base64:b1YCwKwjtfKcdEhWErMhcpSdbq6mnSLxQ/FyD7+BS00=" + "data": "base64:hy0MkWMBBDhX6+R3MGn0JbTfkKERwNppxdGtk6wP4lM=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:THozjLGsLirG1KCXti9H+3KIEudGu+zoJTHdzWpekaU=" + "data": "base64:nTLdhzuZWaG5M4ewLOG2RbsNywrKnVIoPLFk6tOHzI0=" }, - "target": "878703931196243590817531151413670986016194031277626912635514691657912894", + "target": "878277375889837647326843029495509009809390053592540685978895509768758568", "randomness": "0", - "timestamp": 1681340311734, + "timestamp": 1682373467873, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 7, "work": "0" @@ -150,25 +150,25 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA8fcWvUpYJr75AbC9L1ALkhUZKmgD3+8Ump42rq6UFbyke9paBz8L2mykSGFOC29d2cteYmLaETvPg7Elh3t2LKnKXzxrIhJhppA6nsivn6iU4HON3fwqGpJAOG+GLtGG4tjlv8DVSJdCQ3UfjmsPZcaQWpka41S5CTrpxYkJAM0Iejei9j6j7AvBxyBQzAZn3eta1zviiD1NtzOtMZuXPiQM9TJASXeDpH8sqn9q79erfetYP2F6oYe3MfXRbv3IJyO5PGiK2uYP5TGdk1y/1MyJjKoynCeacWEPENYobkbAWPWYMHzO9uhs67pxo/2bcgUfchzb3gzUn5yGtiy75inZhwrYAc81cUBI9yZ28R6AykRXxsVC1H65+bmgNLovfdaTkif26zfBPV2zyxLHySSSFMMlfunTw5wCb8L+VCsuhPf7LltTGpXt7XQ4eHcE1uspuypL6O/GK0ysDALS9JyTThYQM5xmJSYIQdI49UzrdwyvO3dElkWYnJ+OpJpR3p14iFjJb9fHhx1cCtA/BlsEGfekvNrFwiN5jB+rfq0jcehn+Vnd6f5wco58JxHskZrJPKZuPvEea3BKDCblWMl+7W/q91O2hDKkRpLrbFynOjOfVAHPyUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwfFNGyGmVsklhRUbHS7XLC0nJrPGN9kfNtGZPj+SNoYF12j+ZuTjYNqg081Z+Uflz7/yCJG1dEOQZHVlisZgBDQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAEb4Qy9EmO1KCRFbQCevHFM9YhqGB/+w3LBS2HFy55hSrKzin7t9W0jwKuji4j13kJr2Vjz6zGxit/pCy/o1wzZ2meii3EbCoqg/AlgzNvDqMnO3nFFjJTiPy1JxQxmZanSgcBNmC51ru6vAtotGD0kzgYvuBBwk+uFkDlawpmCkH96U/0p2gphqZIKVvfG9wbx1iihupIq1th3+ttLtQRvLWhcL2wAFf4iqrYcAL0nCX1hzXaFIkQPlY5wxeAVqBCZPH+iiVMuyOpTPaAa8Xejrx5Gji5pL6BnzI13m1FCjTeUk/LDFRuRoG1WiAP2i5PqHj0hOenD9tuZrg0I/jjxgxeM4ZoJgugN0I5wYG1WXOv2+lbvk+tp5K5SQohv8PXzlBvB7xsXKZb/mUJ9ewOGRCE24TGOylRzTXEg80gGoaGqaVvkYLhZm7drx0r6x7F07vVUpwLFHHY19lio7OFk32a7hNjQDsKnxjc6k0CSDVlR5vH/pBj9iYLSouSOrdQ9yAQ3IxdHZxLIjKVUbS9OpwJTyRcmnwOHHd57O9rUfWRL2hv/VKSaPt5Ud0gigRhr+mndc2BTxG10Jyd47Pm7Lm6KDu2HFb2+dt4rTToJrut4SsGJUCNUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwgBElhbQg5lQkNWam0MPHAQmFRTBDAeK2Z5SyEyEWisedw1jsKyX0eR0GwK25HJs1faAJhbw7hevUzQnKFo9OCA==" } ] }, { "header": { "sequence": 5, - "previousBlockHash": "209D721CDFAB205814B2610B4ADC64CF3B9F6551AE18A13471AC7D0357B2FF97", + "previousBlockHash": "E384728DEBF8A850C87341655F0727D5FF20CE6A27E93455F9D71209879D2CBE", "noteCommitment": { "type": "Buffer", - "data": "base64:fQ/dbzvAYNj3slxBiYm61P5grUN9Mln8Ki3LM+HdU2c=" + "data": "base64:uZDUHMxifx/lLYAMvi3vTmQnUPVDJsn3EutGP6X7RGc=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:gi5L5VM3BEKwppkzS4NOSq/octpPVoNHb5QrBOA4JM4=" + "data": "base64:88873jI612VcWrkD35ZVZ4UJflVo/fJ5UuBbPsuL8AU=" }, - "target": "876150796287198815250991109327239012206946009879241555988631840253579976", + "target": "875726715553274711274586950997458160797358911132930209640137826778142618", "randomness": "0", - "timestamp": 1681340314059, + "timestamp": 1682373468212, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 8, "work": "0" @@ -176,7 +176,7 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAM8lTrNuP+yRzuz8IuHncvJBbbkFF0gIMlOXHXzCF3j6l/JcCIMJJeWb2bZElJfzVTyR/JFfR/cK35Wu7TLZRgUoUeMD3CGhcYwpJkwRLwFSvZifsuoT6H8EiJOXpO0RAUT2op8hibLyvfNlcpr4g3+1Wz8ey62+CV38VF6+fRXUWpRACqsrzOMHn1Tatu/UV+9aeoiLkUKoplCiaEnzbsTjDYAfSL/jxG2NnOkvA/yGnF7pia3JBMl01oVUHaGr6x1awwZ7bVDSPzT8CpXck8XBtVQzzbKwtqjdfy/pLiTZjihyc0IwAV4qBJOvBtA2HqLq1sFIRtHOs9VS4fQvgRavej6F/+NvbZZHE4FTOr5k3Bw4Hv4GEJ2YID5ccFBlUdor/bWXQsn8yuy6kz+jJIcXwX49U7wevWppLP2a1vxuWdxKuRfatiA2f+f7UM6rdgy7wL3nx7V/dbIcQ9UXQe4ytWN0vOwkUVwmm3//o+KNcGbtgjFg4BBvEkvIWHv29DKdpkhCyK3JTpm1KQ/z+O9fUHDVcu3tpFB8496/03XY9Y5cuBXkBSmuz2OzttmU7jIvjyhlqHRoc4oV/faCf4otMggamyR5TimV/jX64dF+yG4xqUaiMYUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYTgYE8oHf4J3mVHpuLNhAHHJ34OS7VCPZCUkLvMx+qBq9tnJ4O5zX+YGUDA2odRgDGRZpR/eZelt1+kDtYoiDA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAwWTlt9qbZbXiMqtsHfLbJU2ovl4aHD3XBk7gDwYc4piS1Sq8hmGVUnH8kmpAdsPtupFmQ6hWibHwHsKTLGAb5nys5uWqnl4xr4qjM1q1Y9it4JjzN78tbvBDAqPwy8egu8KNCIXhJTzq64KJaHanJW2Pp/1ybR2fje2+lewzIsYDuf/hL255uaLByVhO4llpIfoRhRo9KOYOxVIXOhRzm5GAJINL+L3o+CjAybGtD122OPd70itWJ8vRi01puN4pRV3KOasp0mesp7bHkAdH26cpUR+ZC83plMKgXzl7yp5p2FNcOikSali7JuQvhTiB7I73USnZFtRI/um4h727VetPfJ0eW7FtaxLtyg9qqPPob6YmRfLvQZjS6wcZF5IYjpfSgtIAC/w/uxwX5zL7ib3P7mYACkU4lbFidXRWISEWrjS+RP4/iC3VNbL3mFPKtF483oluc/pGipvDVLIshB4RM6NasxxOXXgffCwleanhcwndW6GFKeUtVPh9J/yddeguinZLOwnf4kxttDbe0tkW/DGQ16vd5L4XGP/ZWUy7/UUjdk/VHiOvqWLqem6Yw7ZZqShQQk/3wl0RldNes9LfvxQSWLT8nl5AN6PxDZ+/7R2a1M6tcUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwO48FpHzh5BaaRS3gOBtYLdCLBCLkbffyMdxildaG6OFP7k+LV+KpfeO2VdGd6TC26TZqel9UUVQcVd9wEriGBQ==" } ] } @@ -184,17 +184,17 @@ "Route wallet/getAccountTransactions optionally streams transactions with decrypted notes": [ { "version": 2, - "id": "6257d28f-2589-491a-8dff-82ba100ef05e", + "id": "5fe8f6d9-1ece-48eb-8442-9fe77e5321d1", "name": "with-notes", - "spendingKey": "b843622fea2334c35a2135429d3aa5596ded1bced783a319e7b92aff10550ef4", - "viewKey": "402baaae1524f08090cc17a4615ea3c96b8fbea5f323b6bd4c349ee688effd453c9a425b434b1013d5b8676f2c8b082bb41eba4377171b8c947f57b91bdeb7b9", - "incomingViewKey": "41de6a61741b8c2fa0383994faa15a0a4880fd361a3299c20406c0e3d02bc305", - "outgoingViewKey": "3866253b6bbbda7419ad9ffe0c3c20d1feda2861d8d5258618278378f5178ef8", - "publicAddress": "f8416b375b30b57d2de4c1fa9c8e2cf9ff99d218f04fe44b2ca2bbc371610263", + "spendingKey": "6790949d45e8641db05a4f12074da5e1710f22401dc33b923e634d2108d606a8", + "viewKey": "b72377d60eb6b21902d7e8b9370b8549df54a88993b47c42fb73c85dc1d4e66f16db624bda1a81e09a8f07acaf23953949887c363e3ae8aa02581d76d1d28562", + "incomingViewKey": "57999426399e805872f195b76e0e88117bad0c0e28c37bc3b6d59d5dff5b6b05", + "outgoingViewKey": "d94c3cafa1cadea3100b2026adafab0d01dd92e2073dbeb9bbe771d7fbb58c1a", + "publicAddress": "7c50f3ada423c06c62931421829968c5fbb5a18e7c968451870dae49d85a74a1", "createdAt": { "hash": { "type": "Buffer", - "data": "base64:psoOrmWPvH7bjorZzuBt1gEp/kEpyedaWdN3dKbPXfE=" + "data": "base64:NqXFasdgU7qM7mFrnp+Ew/x8QzCVac2HsIIgZuvY8Pc=" }, "sequence": 5 } @@ -202,18 +202,18 @@ { "header": { "sequence": 6, - "previousBlockHash": "A6CA0EAE658FBC7EDB8E8AD9CEE06DD60129FE4129C9E75A59D37774A6CF5DF1", + "previousBlockHash": "36A5C56AC76053BA8CEE616B9E9F84C3FC7C43309569CD87B0822066EBD8F0F7", "noteCommitment": { "type": "Buffer", - "data": "base64:lz+X23SkbOEC8hgO5SrpoE4/cv3vYBVrOo+cFbujExs=" + "data": "base64:2lQqfdPeGwXaF6KKXcgUtP5f66JjFiNLv0oMZcvzl0A=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:qU28i/czL3Oij4iyXcAQe4e0jSj8w7TBHK99t0v8lTw=" + "data": "base64:he+LTdiYAMeqjkTTv1bdEtM7pInrGmk22PGDmPMs8Dg=" }, - "target": "873612455013551691691596639672017653407698459874762826227196885622232086", + "target": "873190827380823143577845869093025366895436057143163037218399975928398962", "randomness": "0", - "timestamp": 1681340316797, + "timestamp": 1682373468558, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 9, "work": "0" @@ -221,9 +221,58 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAnrqOopNJgSn+xg6vnFaj7Q7z8RngUVCCYK/Xw0r7b8ilj80nYo4K7YlOpujlSyDa4LyE8P9xHZK111XFDwBQgsEQnOYswtJce8ovS5PQRnOWenEgvjL5jr13rJ0fbPKpVlD/eGE5RVImzfkPG1otZphF0U36BnHZ9uOR5zZ7500T4kBu6veetePvVMsW3Iex1MMFdgFOZDmelH+9zpQuN85m7Cl9c+WnaAVtIQtbRru4Taufif/rDHGn9PQ2t5VUN1HgVUlgzGYUDATdzmQM4K51UA21rSyaHvggUjwyo7/x0j0bEAXewalYEpuBDOz7K68YTuCP/BpbS46yp1aOA4IS7BXTKDPkVuKb3Xm9YqohJ9pQ0Fc7vEs5FfA/fkZpPiv1Xt/Ne7JQHoJUq94uBFA4Y2QUcUSuNiUsmgB2yD535R1ucUB7o5dhXCOeKc5D7EE5xxeD3sFpTiem6G1H2ycjcJDRbVv1tQYrO15s8WSOe+LUKCr3qFTWHsWP413qb7JT2AGdfRNgfLWWiBeCXyV0fc1+fyKQip4qa4WsTcC5OqgAl8W+M1tc7/6Jcwc2lj7OlmcdIb5LKEOH/fHY+6WGjluiiWMdaPsCV1jdcmEmZTrAxi3G20lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw3l5W+KmDU5S8e+4uDo4/+xsnSIKI/VpHHO8OjlSDvwQZBeXfGCibEyBwOmEiCvtxqn8F4B80u/abvfpTcqU1DA==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA0ONxzBWwL1nk9SVh6Cu274s7UYLiMSO417vcMoE8JyOKScUKavsbPMsCD/kF7Zlo14OybSRrleIljvuRbhfqhKeCUXMmd5CQQQydIXaDpNyYCjJDQuwcgDWjy9EvvdRYjh1P9lNVra8MaK00xIoYstv/pNM6cXKtkB5w+09TSqoPuCNYXudNmvNpFWfR/gK2/eUq/kM9dN88F7HlHRMmjC/e4fiZNrtuyWIDxlnxFJaEDCHfQpnaweTYeWTGV0Q62G5/zKrMNqtUea6uOL5WxYjDhVPZDaDJKoFgZwaSNub30TQ3HoN5ygHmS4wW78XMXZnFdP9pM+RPTp8DG1puv1c62wyKPusEHkN6q/j+CX0Vyljasc/PUmo1Jla8PUYUEgTfyJC4Wl/sTvzCh/npYvmvw0HHy13B7gGLX1tTWJu5sIQTLD7fmSsZurRMUPjALHuV8JJbdz5W9mjv649YuXWrup/aSedwGf6A+BXq6Q4ek54pErs9k4ZiGtfVO1LYDguVTpv7NeRXLY6ouf+tKpoztLihX57B+eOtQKsUGJvdWzL3TuNs/Q33PNv2OExgDA2JWv/L9wUMM0j7DOZ1FRHA2cq2AXkIxk/8WanzL7569Hc6weEH0klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw3w7C2vNU+gGn/T6jUUoGeD8QdfwkIZ2gjtfmRqB87SDQYA11MMWFQ1uvboplkTjc5S6AYq+PSY0oivmObLtbDA==" } ] } + ], + "Route wallet/getAccountTransactions optionally streams transactions with spends": [ + { + "version": 2, + "id": "22e7f4e4-5d30-41ff-a0f5-b0a448a51b7a", + "name": "with-spends", + "spendingKey": "ef12de800ee70fc01dec29d525adade6240926cbbc280194763da70f009a6a7f", + "viewKey": "cc8403bfb2b78be629f0c4821448a09cd7291b0a0a8352d3f9dbf60b99348d9e562591ca8e30212dd0f50efdcd0437fb0d1874d36855eece773057e98c4ce9de", + "incomingViewKey": "f28c67a503160bbc6df7a4a59901c6093e2173a05725fe6936aa7cf67656d507", + "outgoingViewKey": "a459bdf180d09cbc5e690fb0da32d45e8d986528d284aeb158ee2f20d04b6f66", + "publicAddress": "2be6febe1ff9c5e53cd10c8aa3a6c1a312dd8236f7c383f6a1ebc580912463e5", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:q6O7uUG1Ytc4H4JuTGPsFgB++sUGFwLBwYZiT2jRHX4=" + }, + "sequence": 6 + } + }, + { + "header": { + "sequence": 7, + "previousBlockHash": "ABA3BBB941B562D7381F826E4C63EC16007EFAC5061702C1C186624F68D11D7E", + "noteCommitment": { + "type": "Buffer", + "data": "base64:PJUaoYMYkoHZj3k2tWe8qUTclEnbe8uy311tdM6lygQ=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:rpD4nJSeVSwP9cI7DZLoifJwJyrAhvynJEGrJYiRXxw=" + }, + "target": "870669583413409794751345832897376592977547406352566801307278513052763546", + "randomness": "0", + "timestamp": 1682373468898, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 10, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAARC6Nx/iWJEWXPxuyztMcHWRb+jliyKSjti1wXkn9k/O3rubcq6m0/9Do583uVp5wCyDpodZcReWRi6ln+IYb123BRKLfl3Ug1AetvKiXo8aPwUdhKaCwwd9TejEKXop4Hn59WtVl2jgbl+JhdPLxU4tcJWceOQKm5RDAd6N9Nc4ViAhufMbJlZHjUGvG6FCF4mm7G/dBeDYmJNGkgRg1LgV3fnwS9dRfkMjqeqPp672MSwkDNREuTTtyWM6ghE8JhzITnJFspvaJA6QzaXLxnwMpEY/fjzVlZsbxUb0IEBEimB0JguOLm7iC6sY8ATKzyLdqdQufnQHTuMpCP75UaaAF5UYV+GuiaeHcDAkaROePLyUGpFusD+fTUogoZFUAqUCeXF/sO599Y8asXA/82bUEOVdpRfZMc5arQMxtOYuKTufP2tnwAEapDIiiIm0nwVNQDKjW0Q7Kx4vYArvHvoxdEQo4cNgbC8ZOiqnvLyxm4bOkDcoEBo/YrCxDbN8vDgY4OXavsK0ZRrQLGD6iiNTPvkg54zAzUGcXoyTwM5+UYSY2NdTJPABoVwa810YOnlnM7Uet0fTwHEblHQ/ndhbeb9Xk2MYB93aiqYdCet2Tu92IfngDPklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwqmUEMNEg6EqpPNqCkTccoKuQ0AKmUUNXI9clbZo9dzPMkQ352NZO6ThYZjV1qa+UqWQRlzhZjqkpbQEt6LumDA==" + } + ] + }, + { + "type": "Buffer", + "data": "base64:AQEAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeW1v+czpqQK+sgQKx0ykuatcH4OE+Vvl2fyto/i0ap2OtbX7EUs8QRy0twkavljLH/UgDhlCtsB+uvhCsTLVqJ5EEd/tvYoZtmcGnvSWuj+DN0twhx0YUrUXBBuYrQBVZRNlrYaX5ScDdC069ZZ3t0HG9UCK0s6XN5HR3tkAFUgLSx4QuMU+T7q3zrfiwSodY7CrjE/jqqxXtMTVL1u+ROxCWvVrQZ7b+/4U0wkkpZqrp1E2NOXQsyaNd4ChGC2HDY0LpWAMwzraZBQAWdJc6arPY1kyFkGA91V4rNLq9y9D3l/gVJrGUKHKl9pNQooZmczJnVJ6qib4rQ9n5IA+BTyVGqGDGJKB2Y95NrVnvKlE3JRJ23vLst9dbXTOpcoECgAAAIisd1Rqh2isVjf7P/5QSHHy5wmGH8nlIZDOEgEal3nnwv6u2s0YVEunbi7hIURphWTD1jUJs9DU5UsjM8IPNCrEL7vBskva4nalCj+hLfw3vVj6nyM/Sc/sB2kOR4IiDIpAgfqVGCl+/DWb3nVGQuOy9Vc6lllC5LAXAJ+qLkSmuyMy5UB4ys/khNbCkNPz4K4YyI7F5Qfo7Ep9UQHIqtVbmMMu5WOFmfOMIUE2PPZTiTBu4heWQ2v8Khro0jYGQQmbtbz4+wOFhnb9qbIN4+GUaGKUTaw/3q+DFZ5JsxJPoc1scYiDsD9G6aL9vbl9ToITrslwFmdul2Nd7C5+WZw+fWs2J+/n+ETXszhfa73QnzAXPCd/dZbF+/9ywBUuVjqcSH+fp9aiOPKiPcjm4WIqQPl9OnuJ0WIondFH220/Q4EVO4+95CsjT5owlp9Yl1lvlSIIYJaHzxtwEkRD8m2C8d21cyOYT5WEmw/xwQVpZi6XFIaU7q1tnseo2j1NvkFLbrK04ucaN6K2hyLZp7ssoFUUWTNe4qITUNZf/xxfRNxB6McP0Z59ASkvm5tpU3mOuAN3f6SY7NZOSudczfCJyq6c14bwfvVo8I9U6WcXHnj4a/RJ9isZqpombmDXrBIoOQwKkViKFucoMImcnezsdyb75PZegJ9yACoaSIfyl0QzwjtSotL/ZoXTmsrMUEKMlWhXie/GGtAYTL2NFBRhmEc8a7DseSdQ64Pl4M8g8+eDWo8GgiWiGkI8/5VGKRbVCV7zIzbCkvxt/VaczagRXl8Om9o5lgj3J00Y2J851Cppjg8wyQOF2i34lji3gAjNH8rkP9HhfyKCNZyFpFQfmzYy6LDIjAZV5cwBtvlv1UzY6WtcgEK2CNfFLfISM5turZEJ0I78iZEe3s4J43Ly/8pJlEsSZNUcoRnMH8VRSxh961dYTFoRkHRswCBjcZ6zbcYEs+NY6pY5t9SrXS3+fPDwlT/K5McZrXOYMw00xxjkym806u+z0ReQuxREiq8bGA/8m+UV+dqh2zAvQzJjUFqwPauOxXO0nBpTVPK9B6k0rSkbuGPKQPSLBzuPwjmx+/fgtg6Ygu0oN2N440txlhQ3xW6K4vWknJe3twc/sTnIeor4z9a6NGxEGKgnDeXbz/lltv0ViooARLWOsXx0BClYjXXHmf/3PGjNBQUtYm/zf62eWeW/KcnMSqxSmFFStfY96VW/NqGO6LgQNASSyLAdfartaB/TTfV5lR2rXPdqLtmxH3Q7EqvKqXljtAkKcp+fvxmREXMF8qhktIOcO72gY6rN7uDuB6xLNwb6dZpzdGsWC3FGsTnAWQZD/7qVHd/o+cWQyYe5vON0XeYv3EEIrzfUJG14sJZKqhm0IH20Yad4KJSx5JNQqpm8ZL7cBpFtwgLmJE5Z4iOmtsNJD5t/wTpzR7SCz6/72nIyi07KDe6GiA6Rg06kYuH9UI0pLbAOhChUvw5ahC6Iyqu/6zlTwuqwo73ynmbjkREiA70pd7qkpRG/4gnKASiE4u9jj20y1j22IJEzfKc4FUkzMxU1eA4UzApYIV8PLdpQtaS9/PNp/Eqt5RLScf5EBw==" + } ] } \ No newline at end of file diff --git a/ironfish/src/rpc/routes/wallet/getTransactions.test.ts b/ironfish/src/rpc/routes/wallet/getTransactions.test.ts index a7dea90905..dfd3047ad6 100644 --- a/ironfish/src/rpc/routes/wallet/getTransactions.test.ts +++ b/ironfish/src/rpc/routes/wallet/getTransactions.test.ts @@ -7,6 +7,7 @@ import { useAccountFixture, useMinerBlockFixture, usePostTxFixture, + useTxSpendsFixture, } from '../../../testUtilities' import { createRouteTest } from '../../../testUtilities/routeTest' import { AsyncUtils } from '../../../utils' @@ -139,4 +140,40 @@ describe('Route wallet/getAccountTransactions', () => { expect(responseTransaction.notes).toHaveLength(1) }) + + it('optionally streams transactions with spends', async () => { + const node = routeTest.node + const account = await useAccountFixture(node.wallet, 'with-spends') + + const { transaction } = await useTxSpendsFixture(node, { account }) + + const response = routeTest.client.request( + 'wallet/getAccountTransactions', + { + account: account.name, + spends: true, + }, + ) + + const transactions = await AsyncUtils.materialize(response.contentStream()) + expect(transactions).toHaveLength(2) + + const [spendTxn] = transactions + + Assert.isNotUndefined(spendTxn.spends) + + expect(spendTxn.spends).toHaveLength(transaction.spends.length) + + const expected = transaction.spends.map((txn) => { + return { + commitment: txn.commitment.toString('hex'), + nullifier: txn.nullifier.toString('hex'), + size: txn.size, + } + }) + + const got = spendTxn.spends + + expect(got).toEqual(expected) + }) }) diff --git a/ironfish/src/rpc/routes/wallet/getTransactions.ts b/ironfish/src/rpc/routes/wallet/getTransactions.ts index 01bad7afbf..6605705777 100644 --- a/ironfish/src/rpc/routes/wallet/getTransactions.ts +++ b/ironfish/src/rpc/routes/wallet/getTransactions.ts @@ -9,7 +9,7 @@ import { Account } from '../../../wallet/account' import { TransactionValue } from '../../../wallet/walletdb/transactionValue' import { RpcRequest } from '../../request' import { ApiNamespace, router } from '../router' -import { RpcAccountDecryptedNote } from './types' +import { RpcAccountDecryptedNote, RpcSpend } from './types' import { getAccount, getAccountDecryptedNotes, @@ -25,6 +25,7 @@ export type GetAccountTransactionsRequest = { offset?: number confirmations?: number notes?: boolean + spends?: boolean } export type GetAccountTransactionsResponse = { @@ -44,6 +45,7 @@ export type GetAccountTransactionsResponse = { submittedSequence: number assetBalanceDeltas: Array<{ assetId: string; assetName: string; delta: string }> notes?: RpcAccountDecryptedNote[] + spends?: RpcSpend[] } export const GetAccountTransactionsRequestSchema: yup.ObjectSchema = @@ -55,7 +57,8 @@ export const GetAccountTransactionsRequestSchema: yup.ObjectSchema ({ + nullifier: spend.nullifier.toString('hex'), + commitment: spend.commitment.toString('hex'), + size: spend.size, + })) + } + const status = await node.wallet.getTransactionStatus(account, transaction, options) const type = await node.wallet.getTransactionType(account, transaction) @@ -188,6 +209,7 @@ const streamTransaction = async ( confirmations: options.confirmations, type, notes, + spends, } request.stream(serialized) From 27b0bbe5d8d2fc87dcf5672a6b23bc2fbd1090c9 Mon Sep 17 00:00:00 2001 From: Derek Guenther Date: Tue, 25 Apr 2023 14:49:48 -0400 Subject: [PATCH 12/19] Deserialize fee as BigI64 (#3847) --- ironfish/src/primitives/transaction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ironfish/src/primitives/transaction.ts b/ironfish/src/primitives/transaction.ts index 61ecea82b8..618861c199 100644 --- a/ironfish/src/primitives/transaction.ts +++ b/ironfish/src/primitives/transaction.ts @@ -51,7 +51,7 @@ export class Transaction { const _notesLength = reader.readU64() // 8 const _mintsLength = reader.readU64() // 8 const _burnsLength = reader.readU64() // 8 - this._fee = BigInt(reader.readI64()) // 8 + this._fee = reader.readBigI64() // 8 this._expiration = reader.readU32() // 4 // randomized public key of sender // to read the value of rpk reader.readBytes(PUBLIC_ADDRESS_LENGTH, true).toString('hex') From 5d087f8e852f42d0134e619fcce733729f69d68f Mon Sep 17 00:00:00 2001 From: Derek Guenther Date: Tue, 25 Apr 2023 14:50:09 -0400 Subject: [PATCH 13/19] Rename RPC route files to match endpoints (#3837) * Rename RPC route files to match endpoints * Try fix fixtures * Rename another fixture --- ....ts.fixture => exportChainStream.test.ts.fixture} | 0 ...exportChain.test.ts => exportChainStream.test.ts} | 0 .../chain/{exportChain.ts => exportChainStream.ts} | 0 .../chain/{followChain.ts => followChainStream.ts} | 0 ironfish/src/rpc/routes/chain/index.ts | 4 ++-- ironfish/src/rpc/routes/{events => event}/index.ts | 0 .../src/rpc/routes/{events => event}/onGossip.ts | 0 ironfish/src/rpc/routes/{events => event}/types.ts | 0 ironfish/src/rpc/routes/index.ts | 8 ++++---- .../blockTemplateStream.test.slow.ts.fixture | 0 .../blockTemplateStream.test.slow.ts | 0 .../routes/{mining => miner}/blockTemplateStream.ts | 0 ironfish/src/rpc/routes/{mining => miner}/index.ts | 0 .../src/rpc/routes/{mining => miner}/submitBlock.ts | 0 .../src/rpc/routes/{peers => peer}/getBannedPeers.ts | 0 ironfish/src/rpc/routes/{peers => peer}/getPeer.ts | 0 .../rpc/routes/{peers => peer}/getPeerMessages.ts | 0 ironfish/src/rpc/routes/{peers => peer}/getPeers.ts | 0 ironfish/src/rpc/routes/{peers => peer}/index.ts | 0 .../wallet/__fixtures__/burnAsset.test.ts.fixture | 2 +- ...ixture => getAccountTransactions.test.ts.fixture} | 0 .../wallet/__fixtures__/getAssets.test.ts.fixture | 2 +- .../wallet/__fixtures__/getBalances.test.ts.fixture | 2 +- .../wallet/__fixtures__/mintAsset.test.ts.fixture | 2 +- .../__fixtures__/rescanAccount.test.ts.fixture | 2 +- .../__fixtures__/sendTransaction.test.ts.fixture | 8 ++++---- ironfish/src/rpc/routes/wallet/burnAsset.test.ts | 2 +- .../wallet/{getNotes.ts => getAccountNotesStream.ts} | 0 .../{getTransaction.ts => getAccountTransaction.ts} | 0 ...ctions.test.ts => getAccountTransactions.test.ts} | 2 +- ...{getTransactions.ts => getAccountTransactions.ts} | 0 .../{getStatus.test.ts => getAccountsStatus.test.ts} | 2 +- .../wallet/{getStatus.ts => getAccountsStatus.ts} | 0 ironfish/src/rpc/routes/wallet/getAssets.test.ts | 2 +- ironfish/src/rpc/routes/wallet/getBalances.test.ts | 2 +- ironfish/src/rpc/routes/wallet/index.ts | 12 ++++++------ ironfish/src/rpc/routes/wallet/mintAsset.test.ts | 2 +- .../routes/wallet/{removeAccount.ts => remove.ts} | 0 ironfish/src/rpc/routes/wallet/rescanAccount.test.ts | 2 +- .../src/rpc/routes/wallet/sendTransaction.test.ts | 2 +- .../src/rpc/routes/wallet/{useAccount.ts => use.ts} | 0 .../rpc/routes/{workers => worker}/getStatus.test.ts | 0 .../src/rpc/routes/{workers => worker}/getStatus.ts | 0 ironfish/src/rpc/routes/{workers => worker}/index.ts | 0 ironfish/src/webApi.ts | 2 +- 45 files changed, 30 insertions(+), 30 deletions(-) rename ironfish/src/rpc/routes/chain/__fixtures__/{exportChain.test.ts.fixture => exportChainStream.test.ts.fixture} (100%) rename ironfish/src/rpc/routes/chain/{exportChain.test.ts => exportChainStream.test.ts} (100%) rename ironfish/src/rpc/routes/chain/{exportChain.ts => exportChainStream.ts} (100%) rename ironfish/src/rpc/routes/chain/{followChain.ts => followChainStream.ts} (100%) rename ironfish/src/rpc/routes/{events => event}/index.ts (100%) rename ironfish/src/rpc/routes/{events => event}/onGossip.ts (100%) rename ironfish/src/rpc/routes/{events => event}/types.ts (100%) rename ironfish/src/rpc/routes/{mining => miner}/__fixtures__/blockTemplateStream.test.slow.ts.fixture (100%) rename ironfish/src/rpc/routes/{mining => miner}/blockTemplateStream.test.slow.ts (100%) rename ironfish/src/rpc/routes/{mining => miner}/blockTemplateStream.ts (100%) rename ironfish/src/rpc/routes/{mining => miner}/index.ts (100%) rename ironfish/src/rpc/routes/{mining => miner}/submitBlock.ts (100%) rename ironfish/src/rpc/routes/{peers => peer}/getBannedPeers.ts (100%) rename ironfish/src/rpc/routes/{peers => peer}/getPeer.ts (100%) rename ironfish/src/rpc/routes/{peers => peer}/getPeerMessages.ts (100%) rename ironfish/src/rpc/routes/{peers => peer}/getPeers.ts (100%) rename ironfish/src/rpc/routes/{peers => peer}/index.ts (100%) rename ironfish/src/rpc/routes/wallet/__fixtures__/{getTransactions.test.ts.fixture => getAccountTransactions.test.ts.fixture} (100%) rename ironfish/src/rpc/routes/wallet/{getNotes.ts => getAccountNotesStream.ts} (100%) rename ironfish/src/rpc/routes/wallet/{getTransaction.ts => getAccountTransaction.ts} (100%) rename ironfish/src/rpc/routes/wallet/{getTransactions.test.ts => getAccountTransactions.test.ts} (98%) rename ironfish/src/rpc/routes/wallet/{getTransactions.ts => getAccountTransactions.ts} (100%) rename ironfish/src/rpc/routes/wallet/{getStatus.test.ts => getAccountsStatus.test.ts} (95%) rename ironfish/src/rpc/routes/wallet/{getStatus.ts => getAccountsStatus.ts} (100%) rename ironfish/src/rpc/routes/wallet/{removeAccount.ts => remove.ts} (100%) rename ironfish/src/rpc/routes/wallet/{useAccount.ts => use.ts} (100%) rename ironfish/src/rpc/routes/{workers => worker}/getStatus.test.ts (100%) rename ironfish/src/rpc/routes/{workers => worker}/getStatus.ts (100%) rename ironfish/src/rpc/routes/{workers => worker}/index.ts (100%) diff --git a/ironfish/src/rpc/routes/chain/__fixtures__/exportChain.test.ts.fixture b/ironfish/src/rpc/routes/chain/__fixtures__/exportChainStream.test.ts.fixture similarity index 100% rename from ironfish/src/rpc/routes/chain/__fixtures__/exportChain.test.ts.fixture rename to ironfish/src/rpc/routes/chain/__fixtures__/exportChainStream.test.ts.fixture diff --git a/ironfish/src/rpc/routes/chain/exportChain.test.ts b/ironfish/src/rpc/routes/chain/exportChainStream.test.ts similarity index 100% rename from ironfish/src/rpc/routes/chain/exportChain.test.ts rename to ironfish/src/rpc/routes/chain/exportChainStream.test.ts diff --git a/ironfish/src/rpc/routes/chain/exportChain.ts b/ironfish/src/rpc/routes/chain/exportChainStream.ts similarity index 100% rename from ironfish/src/rpc/routes/chain/exportChain.ts rename to ironfish/src/rpc/routes/chain/exportChainStream.ts diff --git a/ironfish/src/rpc/routes/chain/followChain.ts b/ironfish/src/rpc/routes/chain/followChainStream.ts similarity index 100% rename from ironfish/src/rpc/routes/chain/followChain.ts rename to ironfish/src/rpc/routes/chain/followChainStream.ts diff --git a/ironfish/src/rpc/routes/chain/index.ts b/ironfish/src/rpc/routes/chain/index.ts index bec1b25df1..c53fd72844 100644 --- a/ironfish/src/rpc/routes/chain/index.ts +++ b/ironfish/src/rpc/routes/chain/index.ts @@ -5,8 +5,8 @@ export * from './broadcastTransaction' export * from './estimateFeeRate' export * from './estimateFeeRates' -export * from './exportChain' -export * from './followChain' +export * from './exportChainStream' +export * from './followChainStream' export * from './getBlock' export * from './getChainInfo' export * from './getDifficulty' diff --git a/ironfish/src/rpc/routes/events/index.ts b/ironfish/src/rpc/routes/event/index.ts similarity index 100% rename from ironfish/src/rpc/routes/events/index.ts rename to ironfish/src/rpc/routes/event/index.ts diff --git a/ironfish/src/rpc/routes/events/onGossip.ts b/ironfish/src/rpc/routes/event/onGossip.ts similarity index 100% rename from ironfish/src/rpc/routes/events/onGossip.ts rename to ironfish/src/rpc/routes/event/onGossip.ts diff --git a/ironfish/src/rpc/routes/events/types.ts b/ironfish/src/rpc/routes/event/types.ts similarity index 100% rename from ironfish/src/rpc/routes/events/types.ts rename to ironfish/src/rpc/routes/event/types.ts diff --git a/ironfish/src/rpc/routes/index.ts b/ironfish/src/rpc/routes/index.ts index 632945f14d..649f5b9108 100644 --- a/ironfish/src/rpc/routes/index.ts +++ b/ironfish/src/rpc/routes/index.ts @@ -4,12 +4,12 @@ export * from './wallet' export * from './config' export * from './chain' -export * from './events' +export * from './event' export * from './node' -export * from './peers' +export * from './peer' export * from './router' export * from './rpc' -export * from './mining' +export * from './miner' export * from './faucet' -export * from './workers' +export * from './worker' export * from './mempool' diff --git a/ironfish/src/rpc/routes/mining/__fixtures__/blockTemplateStream.test.slow.ts.fixture b/ironfish/src/rpc/routes/miner/__fixtures__/blockTemplateStream.test.slow.ts.fixture similarity index 100% rename from ironfish/src/rpc/routes/mining/__fixtures__/blockTemplateStream.test.slow.ts.fixture rename to ironfish/src/rpc/routes/miner/__fixtures__/blockTemplateStream.test.slow.ts.fixture diff --git a/ironfish/src/rpc/routes/mining/blockTemplateStream.test.slow.ts b/ironfish/src/rpc/routes/miner/blockTemplateStream.test.slow.ts similarity index 100% rename from ironfish/src/rpc/routes/mining/blockTemplateStream.test.slow.ts rename to ironfish/src/rpc/routes/miner/blockTemplateStream.test.slow.ts diff --git a/ironfish/src/rpc/routes/mining/blockTemplateStream.ts b/ironfish/src/rpc/routes/miner/blockTemplateStream.ts similarity index 100% rename from ironfish/src/rpc/routes/mining/blockTemplateStream.ts rename to ironfish/src/rpc/routes/miner/blockTemplateStream.ts diff --git a/ironfish/src/rpc/routes/mining/index.ts b/ironfish/src/rpc/routes/miner/index.ts similarity index 100% rename from ironfish/src/rpc/routes/mining/index.ts rename to ironfish/src/rpc/routes/miner/index.ts diff --git a/ironfish/src/rpc/routes/mining/submitBlock.ts b/ironfish/src/rpc/routes/miner/submitBlock.ts similarity index 100% rename from ironfish/src/rpc/routes/mining/submitBlock.ts rename to ironfish/src/rpc/routes/miner/submitBlock.ts diff --git a/ironfish/src/rpc/routes/peers/getBannedPeers.ts b/ironfish/src/rpc/routes/peer/getBannedPeers.ts similarity index 100% rename from ironfish/src/rpc/routes/peers/getBannedPeers.ts rename to ironfish/src/rpc/routes/peer/getBannedPeers.ts diff --git a/ironfish/src/rpc/routes/peers/getPeer.ts b/ironfish/src/rpc/routes/peer/getPeer.ts similarity index 100% rename from ironfish/src/rpc/routes/peers/getPeer.ts rename to ironfish/src/rpc/routes/peer/getPeer.ts diff --git a/ironfish/src/rpc/routes/peers/getPeerMessages.ts b/ironfish/src/rpc/routes/peer/getPeerMessages.ts similarity index 100% rename from ironfish/src/rpc/routes/peers/getPeerMessages.ts rename to ironfish/src/rpc/routes/peer/getPeerMessages.ts diff --git a/ironfish/src/rpc/routes/peers/getPeers.ts b/ironfish/src/rpc/routes/peer/getPeers.ts similarity index 100% rename from ironfish/src/rpc/routes/peers/getPeers.ts rename to ironfish/src/rpc/routes/peer/getPeers.ts diff --git a/ironfish/src/rpc/routes/peers/index.ts b/ironfish/src/rpc/routes/peer/index.ts similarity index 100% rename from ironfish/src/rpc/routes/peers/index.ts rename to ironfish/src/rpc/routes/peer/index.ts diff --git a/ironfish/src/rpc/routes/wallet/__fixtures__/burnAsset.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/burnAsset.test.ts.fixture index 30697d467d..0ef4464a95 100644 --- a/ironfish/src/rpc/routes/wallet/__fixtures__/burnAsset.test.ts.fixture +++ b/ironfish/src/rpc/routes/wallet/__fixtures__/burnAsset.test.ts.fixture @@ -1,5 +1,5 @@ { - "burnAsset with valid parameters returns the asset identifier and transaction hash": [ + "Route wallet/burnAsset with valid parameters returns the asset identifier and transaction hash": [ { "version": 2, "id": "538d4c89-d8f9-45c4-ba7f-d5f9cd1ed23d", diff --git a/ironfish/src/rpc/routes/wallet/__fixtures__/getTransactions.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/getAccountTransactions.test.ts.fixture similarity index 100% rename from ironfish/src/rpc/routes/wallet/__fixtures__/getTransactions.test.ts.fixture rename to ironfish/src/rpc/routes/wallet/__fixtures__/getAccountTransactions.test.ts.fixture diff --git a/ironfish/src/rpc/routes/wallet/__fixtures__/getAssets.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/getAssets.test.ts.fixture index e83ba76a02..58c6db8700 100644 --- a/ironfish/src/rpc/routes/wallet/__fixtures__/getAssets.test.ts.fixture +++ b/ironfish/src/rpc/routes/wallet/__fixtures__/getAssets.test.ts.fixture @@ -1,5 +1,5 @@ { - "wallet/getAssets returns a stream of assets the wallet owns": [ + "Route wallet/getAssets returns a stream of assets the wallet owns": [ { "version": 2, "id": "785cb850-450d-433d-93a6-cbbac3fe21dd", diff --git a/ironfish/src/rpc/routes/wallet/__fixtures__/getBalances.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/getBalances.test.ts.fixture index dc5a7e10bf..8824115a21 100644 --- a/ironfish/src/rpc/routes/wallet/__fixtures__/getBalances.test.ts.fixture +++ b/ironfish/src/rpc/routes/wallet/__fixtures__/getBalances.test.ts.fixture @@ -1,5 +1,5 @@ { - "getBalances with a valid account streams balances for all assets owned by the account": [ + "Route wallet/getBalances with a valid account streams balances for all assets owned by the account": [ { "version": 2, "id": "e9a91252-1821-4d33-a020-74b13571ea92", diff --git a/ironfish/src/rpc/routes/wallet/__fixtures__/mintAsset.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/mintAsset.test.ts.fixture index c9eea912ce..f7cfb1e21b 100644 --- a/ironfish/src/rpc/routes/wallet/__fixtures__/mintAsset.test.ts.fixture +++ b/ironfish/src/rpc/routes/wallet/__fixtures__/mintAsset.test.ts.fixture @@ -1,5 +1,5 @@ { - "mint with valid parameters returns the asset identifier and transaction hash": [ + "Route wallet/mintAsset with valid parameters returns the asset identifier and transaction hash": [ { "version": 2, "id": "76143464-09eb-4152-86ae-177602a9128a", diff --git a/ironfish/src/rpc/routes/wallet/__fixtures__/rescanAccount.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/rescanAccount.test.ts.fixture index dce6fd09f5..13c2967fe4 100644 --- a/ironfish/src/rpc/routes/wallet/__fixtures__/rescanAccount.test.ts.fixture +++ b/ironfish/src/rpc/routes/wallet/__fixtures__/rescanAccount.test.ts.fixture @@ -1,5 +1,5 @@ { - "wallet/rescanAccount sets account head to one before the request.from sequence": [ + "Route wallet/rescanAccount sets account head to one before the request.from sequence": [ { "header": { "sequence": 2, diff --git a/ironfish/src/rpc/routes/wallet/__fixtures__/sendTransaction.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/sendTransaction.test.ts.fixture index dae840f218..7df93e844a 100644 --- a/ironfish/src/rpc/routes/wallet/__fixtures__/sendTransaction.test.ts.fixture +++ b/ironfish/src/rpc/routes/wallet/__fixtures__/sendTransaction.test.ts.fixture @@ -1,5 +1,5 @@ { - "Transactions sendTransaction throws if send throws NotEnoughFundsError": [ + "Route wallet/sendTransaction throws if send throws NotEnoughFundsError": [ { "version": 2, "id": "ad611c3e-cc74-4c55-93de-df5c83383da7", @@ -12,7 +12,7 @@ "createdAt": null } ], - "Transactions sendTransaction calls the send method on the node with single recipient": [ + "Route wallet/sendTransaction calls the send method on the node with single recipient": [ { "version": 2, "id": "003caec8-902e-4adf-a8c4-43880defbd76", @@ -29,7 +29,7 @@ "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAmIZBz6LvzUJYezVyBaeMUt9jvVBiQZ7naWLj1a5iwKuX1DMZUPJWjycmVAiu/qoVk+ky8Bpu6Q4d0Clam5DmB25/T/b+OU45P/5EOwvtvJyq9emAc+MTxEcMPseTTcKfI8K4ZDSZprvTg6l/bv707855ytm7NUDeAdLXmycsWroK95a+ARD0qkAJnyIaMuc2Yk4onodyesx3XECcEZk4UdUBjJGShkMiQVnCPYfq9Wih9pl8l8uG1mjarAQFBgDZu1z/9Nc1SAkIIguKU/HpO/KytKzfxYNkLNvyrDjO6PrlkCB9QtzjcNKMA8u53Roh7Vxg+b2twvnt4B40LVVfREH7G0w+JA4gnYWrpASC74OnvYpVkUboZCM0fZVgoMVb1dogeFeqoMe5i6TY4ksdSNfMQ+MYVMWKkNJf4Qw12o90mR1WJPI6UR+XsYMd8LoKcoQJC4tn4H/5qaTRUJPWIxta1CQqVBTiCbw1cCQ1ysiNYs08BFel53goe7l4Kiwf3xRSdFcOeR577VKycw+5Nls8rND3blYTpKZMIJrOvgbYpMUHXmMfQ87LW8m1aVUNlP7efnxR6OYAKyJV8K/Xwt+sLF9c+h1s2mWGjvF4mP0SPYj7Ob/n/0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYxnzEuaXpq4ag1tXweWiArbdBXTDiyBUkeL9NxYJBpx386n97w/0tG0qdwJy9/f9aUb4grAvY+KwOCJtVW9+Ag==" } ], - "Transactions sendTransaction calls the send method on the node with multiple recipient": [ + "Route wallet/sendTransaction calls the send method on the node with multiple recipient": [ { "version": 2, "id": "1832b851-71c7-4d32-8690-7a6b227feffa", @@ -46,7 +46,7 @@ "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAIQUxwVXl4AUZpLx9Vkx2V9TSDhLtU2gcuSF3H/lelFqFakCNpmXOSaL0YDVi8ssZ4khmyGaat2e6wOv/y3lP7f9ddsUpZvJQKaye0nNn/UyY3gLXEODqIcDlpln/ERWRw6vwimT9HmTJTzK0WP+RSnXAhqQU8qOvLUlJY+C1begEA+BesF+Asgm2nuLVxqZ7yOuksi8fQNUoLhxbSgUwez/oUbOKRS122zGnIQNiGEeCIFulSCtc+R+nAOOmHcu7JrgSOdfKJjj2ttWc3x1dD/8V1QhN1ghQrNqKnZZG1Iz12t87f372PIMO8PG0GJ8icZNS+GgoEoGVGbqlDnHEREPQuO9gpbN8xxRh7GAQ27st7kNeMFFASpfI6uiOrL9IM2gwUbdAhcOo+7ljB1hrWyiKHKhbEx4iYRTXbgLMghUX8LlVHggRl0LDzXfONtHk5UrnQdns6o01i4T7JU3g6hFifBDQMUBEjOBJKOE8uX/hjltPM9ZwjQo79WSxr7hbdOmu2I6JDhvHuwLFPdg65vahU2i7wy4phgIACNC+Dgo3DnCyuxnIntLHZm5Z9+qvJS70TN/pErNdW1l3TyuqkOFXA1ERzPM+0J9krDHf+ZOg0aZ4fE8Psklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwoo7jnA8LxCYTuQKSvGbE9rkzjDPr9GYMcH/IvL2wxBnc+gD4CwQJa2dbw7HAV7i8tCZcNHPWqcYt+8DVsoSxCw==" } ], - "Transactions sendTransaction lets you configure the expiration and confirmations": [ + "Route wallet/sendTransaction lets you configure the expiration and confirmations": [ { "version": 2, "id": "55323906-3ab3-4a57-bdb4-22790c0a24cd", diff --git a/ironfish/src/rpc/routes/wallet/burnAsset.test.ts b/ironfish/src/rpc/routes/wallet/burnAsset.test.ts index 6b2c79f409..c704d70b6a 100644 --- a/ironfish/src/rpc/routes/wallet/burnAsset.test.ts +++ b/ironfish/src/rpc/routes/wallet/burnAsset.test.ts @@ -10,7 +10,7 @@ import { import { createRouteTest } from '../../../testUtilities/routeTest' import { CurrencyUtils } from '../../../utils' -describe('burnAsset', () => { +describe('Route wallet/burnAsset', () => { const routeTest = createRouteTest(true) beforeAll(async () => { diff --git a/ironfish/src/rpc/routes/wallet/getNotes.ts b/ironfish/src/rpc/routes/wallet/getAccountNotesStream.ts similarity index 100% rename from ironfish/src/rpc/routes/wallet/getNotes.ts rename to ironfish/src/rpc/routes/wallet/getAccountNotesStream.ts diff --git a/ironfish/src/rpc/routes/wallet/getTransaction.ts b/ironfish/src/rpc/routes/wallet/getAccountTransaction.ts similarity index 100% rename from ironfish/src/rpc/routes/wallet/getTransaction.ts rename to ironfish/src/rpc/routes/wallet/getAccountTransaction.ts diff --git a/ironfish/src/rpc/routes/wallet/getTransactions.test.ts b/ironfish/src/rpc/routes/wallet/getAccountTransactions.test.ts similarity index 98% rename from ironfish/src/rpc/routes/wallet/getTransactions.test.ts rename to ironfish/src/rpc/routes/wallet/getAccountTransactions.test.ts index dfd3047ad6..025348e7f7 100644 --- a/ironfish/src/rpc/routes/wallet/getTransactions.test.ts +++ b/ironfish/src/rpc/routes/wallet/getAccountTransactions.test.ts @@ -11,7 +11,7 @@ import { } from '../../../testUtilities' import { createRouteTest } from '../../../testUtilities/routeTest' import { AsyncUtils } from '../../../utils' -import { GetAccountTransactionsResponse } from './getTransactions' +import { GetAccountTransactionsResponse } from './getAccountTransactions' describe('Route wallet/getAccountTransactions', () => { const routeTest = createRouteTest(true) diff --git a/ironfish/src/rpc/routes/wallet/getTransactions.ts b/ironfish/src/rpc/routes/wallet/getAccountTransactions.ts similarity index 100% rename from ironfish/src/rpc/routes/wallet/getTransactions.ts rename to ironfish/src/rpc/routes/wallet/getAccountTransactions.ts diff --git a/ironfish/src/rpc/routes/wallet/getStatus.test.ts b/ironfish/src/rpc/routes/wallet/getAccountsStatus.test.ts similarity index 95% rename from ironfish/src/rpc/routes/wallet/getStatus.test.ts rename to ironfish/src/rpc/routes/wallet/getAccountsStatus.test.ts index f76764c9bc..98e636c729 100644 --- a/ironfish/src/rpc/routes/wallet/getStatus.test.ts +++ b/ironfish/src/rpc/routes/wallet/getAccountsStatus.test.ts @@ -8,7 +8,7 @@ import { v4 as uuid } from 'uuid' import { createRouteTest } from '../../../testUtilities/routeTest' -describe('Route wallet/status', () => { +describe('Route wallet/getAccountsStatus', () => { const routeTest = createRouteTest(true) it('should return account status information', async () => { diff --git a/ironfish/src/rpc/routes/wallet/getStatus.ts b/ironfish/src/rpc/routes/wallet/getAccountsStatus.ts similarity index 100% rename from ironfish/src/rpc/routes/wallet/getStatus.ts rename to ironfish/src/rpc/routes/wallet/getAccountsStatus.ts diff --git a/ironfish/src/rpc/routes/wallet/getAssets.test.ts b/ironfish/src/rpc/routes/wallet/getAssets.test.ts index 971f73faff..8bdd1ef823 100644 --- a/ironfish/src/rpc/routes/wallet/getAssets.test.ts +++ b/ironfish/src/rpc/routes/wallet/getAssets.test.ts @@ -14,7 +14,7 @@ import { createRouteTest } from '../../../testUtilities/routeTest' import { AsyncUtils } from '../../../utils' import { AssetStatus } from '../../../wallet' -describe('wallet/getAssets', () => { +describe('Route wallet/getAssets', () => { const routeTest = createRouteTest() it('returns a stream of assets the wallet owns', async () => { diff --git a/ironfish/src/rpc/routes/wallet/getBalances.test.ts b/ironfish/src/rpc/routes/wallet/getBalances.test.ts index 5ac5f9fb6c..5d39bfdfaf 100644 --- a/ironfish/src/rpc/routes/wallet/getBalances.test.ts +++ b/ironfish/src/rpc/routes/wallet/getBalances.test.ts @@ -5,7 +5,7 @@ import { Asset } from '@ironfish/rust-nodejs' import { useAccountFixture } from '../../../testUtilities' import { createRouteTest } from '../../../testUtilities/routeTest' -describe('getBalances', () => { +describe('Route wallet/getBalances', () => { const routeTest = createRouteTest(true) beforeAll(async () => { diff --git a/ironfish/src/rpc/routes/wallet/index.ts b/ironfish/src/rpc/routes/wallet/index.ts index 99f648b0d6..eaff7336fb 100644 --- a/ironfish/src/rpc/routes/wallet/index.ts +++ b/ironfish/src/rpc/routes/wallet/index.ts @@ -11,17 +11,17 @@ export * from './getAssets' export * from './getBalance' export * from './getBalances' export * from './getDefaultAccount' -export * from './getNotes' +export * from './getAccountNotesStream' export * from './getPublicKey' -export * from './getStatus' -export * from './getTransaction' -export * from './getTransactions' +export * from './getAccountsStatus' +export * from './getAccountTransaction' +export * from './getAccountTransactions' export * from './importAccount' export * from './mintAsset' export * from './postTransaction' -export * from './removeAccount' +export * from './remove' export * from './rescanAccount' export * from './sendTransaction' export * from './sendTransaction' -export * from './useAccount' +export * from './use' export * from './createTransaction' diff --git a/ironfish/src/rpc/routes/wallet/mintAsset.test.ts b/ironfish/src/rpc/routes/wallet/mintAsset.test.ts index f31fdcf8f9..5e97fd345d 100644 --- a/ironfish/src/rpc/routes/wallet/mintAsset.test.ts +++ b/ironfish/src/rpc/routes/wallet/mintAsset.test.ts @@ -6,7 +6,7 @@ import { useAccountFixture, useTxFixture } from '../../../testUtilities' import { createRouteTest } from '../../../testUtilities/routeTest' import { CurrencyUtils } from '../../../utils' -describe('mint', () => { +describe('Route wallet/mintAsset', () => { const routeTest = createRouteTest(true) beforeAll(async () => { diff --git a/ironfish/src/rpc/routes/wallet/removeAccount.ts b/ironfish/src/rpc/routes/wallet/remove.ts similarity index 100% rename from ironfish/src/rpc/routes/wallet/removeAccount.ts rename to ironfish/src/rpc/routes/wallet/remove.ts diff --git a/ironfish/src/rpc/routes/wallet/rescanAccount.test.ts b/ironfish/src/rpc/routes/wallet/rescanAccount.test.ts index 809609e792..71581d86fb 100644 --- a/ironfish/src/rpc/routes/wallet/rescanAccount.test.ts +++ b/ironfish/src/rpc/routes/wallet/rescanAccount.test.ts @@ -11,7 +11,7 @@ import { createRouteTest } from '../../../testUtilities/routeTest' import { Account, ScanState } from '../../../wallet' import { RescanAccountResponse } from './rescanAccount' -describe('wallet/rescanAccount', () => { +describe('Route wallet/rescanAccount', () => { const routeTest = createRouteTest() let account: Account diff --git a/ironfish/src/rpc/routes/wallet/sendTransaction.test.ts b/ironfish/src/rpc/routes/wallet/sendTransaction.test.ts index a4f6dea2a1..c9b04729af 100644 --- a/ironfish/src/rpc/routes/wallet/sendTransaction.test.ts +++ b/ironfish/src/rpc/routes/wallet/sendTransaction.test.ts @@ -40,7 +40,7 @@ const TEST_PARAMS_MULTI = { fee: BigInt(1).toString(), } -describe('Transactions sendTransaction', () => { +describe('Route wallet/sendTransaction', () => { const routeTest = createRouteTest(true) beforeAll(async () => { diff --git a/ironfish/src/rpc/routes/wallet/useAccount.ts b/ironfish/src/rpc/routes/wallet/use.ts similarity index 100% rename from ironfish/src/rpc/routes/wallet/useAccount.ts rename to ironfish/src/rpc/routes/wallet/use.ts diff --git a/ironfish/src/rpc/routes/workers/getStatus.test.ts b/ironfish/src/rpc/routes/worker/getStatus.test.ts similarity index 100% rename from ironfish/src/rpc/routes/workers/getStatus.test.ts rename to ironfish/src/rpc/routes/worker/getStatus.test.ts diff --git a/ironfish/src/rpc/routes/workers/getStatus.ts b/ironfish/src/rpc/routes/worker/getStatus.ts similarity index 100% rename from ironfish/src/rpc/routes/workers/getStatus.ts rename to ironfish/src/rpc/routes/worker/getStatus.ts diff --git a/ironfish/src/rpc/routes/workers/index.ts b/ironfish/src/rpc/routes/worker/index.ts similarity index 100% rename from ironfish/src/rpc/routes/workers/index.ts rename to ironfish/src/rpc/routes/worker/index.ts diff --git a/ironfish/src/webApi.ts b/ironfish/src/webApi.ts index b3d6146194..99bffa0a82 100644 --- a/ironfish/src/webApi.ts +++ b/ironfish/src/webApi.ts @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import axios, { AxiosError, AxiosRequestConfig } from 'axios' -import { FollowChainStreamResponse } from './rpc/routes/chain/followChain' +import { FollowChainStreamResponse } from './rpc/routes/chain/followChainStream' import { Metric } from './telemetry' import { HasOwnProperty, UnwrapPromise } from './utils/types' From f11b3f8eef36a519e9906f73accba350a86bc9c4 Mon Sep 17 00:00:00 2001 From: Jason Spafford Date: Tue, 25 Apr 2023 15:43:25 -0400 Subject: [PATCH 14/19] Rename DEV -> DEVNET (#3820) Makes devnet consistent with all the other networks --- ironfish/src/defaultNetworkDefinitions.ts | 8 ++++---- ironfish/src/networkDefinition.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ironfish/src/defaultNetworkDefinitions.ts b/ironfish/src/defaultNetworkDefinitions.ts index b87f964ebb..3e8d692818 100644 --- a/ironfish/src/defaultNetworkDefinitions.ts +++ b/ironfish/src/defaultNetworkDefinitions.ts @@ -13,7 +13,7 @@ export function defaultNetworkName(networkId: number): string | undefined { case 1: return 'Mainnet' case 2: - return 'Dev' + return 'Devnet' } } @@ -34,7 +34,7 @@ export const DEV_GENESIS_ACCOUNT = { createdAt: null, } -const DEV_GENESIS = `{ +const DEVNET_GENESIS = `{ "header": { "sequence": 1, "previousBlockHash": "0000000000000000000000000000000000000000000000000000000000000000", @@ -184,11 +184,11 @@ export const MAINNET = ` } }` -export const DEV = ` +export const DEVNET = ` { "id": 2, "bootstrapNodes": [], - "genesis": ${DEV_GENESIS}, + "genesis": ${DEVNET_GENESIS}, "consensus": { "allowedBlockFutureSeconds": 15, "genesisSupplyInIron": 42000000, diff --git a/ironfish/src/networkDefinition.ts b/ironfish/src/networkDefinition.ts index dd918df2ed..c5c97254f7 100644 --- a/ironfish/src/networkDefinition.ts +++ b/ironfish/src/networkDefinition.ts @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import * as yup from 'yup' import { ConsensusParameters } from './consensus' -import { DEV, isDefaultNetworkId, MAINNET, TESTNET } from './defaultNetworkDefinitions' +import { DEVNET, isDefaultNetworkId, MAINNET, TESTNET } from './defaultNetworkDefinitions' import { Config, InternalStore } from './fileStores' import { FileSystem } from './fileSystems' import { SerializedBlock } from './primitives/block' @@ -80,7 +80,7 @@ export async function getNetworkDefinition( } else if (networkId === 1) { networkDefinitionJSON = MAINNET } else if (networkId === 2) { - networkDefinitionJSON = DEV + networkDefinitionJSON = DEVNET } else { networkDefinitionJSON = await files.readFile(config.get('networkDefinitionPath')) } From 5de5e4f3ae7413d568a4218cb9a303ea5a8854f8 Mon Sep 17 00:00:00 2001 From: Hugh Cunningham <57735705+hughy@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:02:23 -0700 Subject: [PATCH 15/19] resets the testnet with a new genesis block (#3848) before mainnet many miners and mining pools were mining on the testnet. the hashpower on the testnet at that time pushed the difficulty very high. now that that mining power has migrated to mainnet the testnet chain is effectively halted. resetting the testnet with a new genesis block will allow us to get the testnet running again with lower mining power. --- ironfish/src/defaultNetworkDefinitions.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ironfish/src/defaultNetworkDefinitions.ts b/ironfish/src/defaultNetworkDefinitions.ts index 3e8d692818..1f86060371 100644 --- a/ironfish/src/defaultNetworkDefinitions.ts +++ b/ironfish/src/defaultNetworkDefinitions.ts @@ -75,15 +75,15 @@ const TESTNET_GENESIS = `{ "previousBlockHash": "0000000000000000000000000000000000000000000000000000000000000000", "noteCommitment": { "type": "Buffer", - "data": "base64:U5b5b7OGqM+0ehLhNYnwTYQB7QAdZRjD8zlPCnDStzQ=" + "data": "base64:Dakcg1h1FK+DnZMH9y2LyqQKCo/N9AvJZHZM8KmeBBo=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:tqHnzQKTwv0RgzwnjHC1xedWkn8wD+hprguXg/r4N04=" + "data": "base64:muDZr1KNyDyRAotRotwv9kDPAZW0Pl3jHVZLlKlHWbc=" }, "target": "883423532389192164791648750371459257913741948437809479060803100646309888", "randomness": "0", - "timestamp": 1681339537127, + "timestamp": 1682450615845, "graffiti": "67656E6573697300000000000000000000000000000000000000000000000000", "noteSize": 3, "work": "0" @@ -91,18 +91,19 @@ const TESTNET_GENESIS = `{ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAk2EuR/JJ6NRgfK7YaF5uT/xKDLEMTdykFmoFjhpDM4WH99W4wVeKVmJgA0TXz31QgV5KDNW3TuS8ySKkCn8zJnn17E+NxVJ1n4w3Ir/y56yOF78pjRSIPHJb8byhDevCwFVkR4bKFLeQGebCSv5aFDelItoFJKOSJc7PX3EKCRMDukrXJZZPr+9o+lixsrOhbxOR6kwbkxNgBaxhBwxT1GgPFiVeTbMUn9D5GESSbi6UK5yGCYGt3jfWrGM6n/0N15B4CYqFwFIjXNlJlTaRzNdv0GFo41z2h0KaWs0EjXN9JWDKMcPux31PWAj5Xb6iKHlmvVLv/omefoLzqWBKUtldDz5UK5dWLpwR0+1m3N0esD71oWXxS5P1RbmMWgoIGav6EJba4O/qpkQaz/oLTihpfZKdI6+CAaDJMUhpSavYljS+rDH+KxsoLMUczpFCmlkzw3G7AKVGkDwkmabSzk6Q3xbZrYDvphX+64VEGTIDB5Cu//QEkHo+IBp1juTIxnCT+0z6fVGm42Ro36NPFJfKzB6s1nNOPSBkY37aGmaofdykzxtizwUXTfVYkCkdqrNZ9I1H9v1uAxL0hubNW/6m7FhzWlYKPrpj4xM78NPx6Tp2T2m84Elyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwhYg4sQPcSqeL5J6q0nWeerrgjUUxcNFo5AwQS2xbbPHKbjuNbWZamWO4d+Lmx9RKbEr4nJrpeV5uE0Dynql+Bw==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIZUFsEBy75DHERvMUoYFnuIrffQDF2Ao/EUafurEQmOy6F6F9ngsxlCQANtoxgyCSXthiOuv+thHLXg6QIfeJBb0/7uFimGu+XD253d7LaqktrTcPCwk7ruie4ybh4XAyiTb9QtlNPkYpZ/GIhv66vBw9VpDp3kFncjwvNhAO5gNWANJaFRFDquWoYjR77eZoEANbJWLgLmuaOmLoCWF7wUaG+qYeMMjbWOEauRirb6Y2mRnr093H5AZHqg4CJER5CxjLNhdIttCS9HH6psGtxRs/o145LxDTayXEpiRtpJOt+3bRyAQFuTri34QJamHsVapsHsy3Tb2ZJEBWnHtiX1U+yzTSWUpbV4t6xRA57sYuS12Jw1hfbmOBrGBrG0MmO/xEDLZ8W2SxtUs1g2rr2huOJqqFMJ53dSI64ZYVcUj7swIBB7EP2M40caN9NftCMBsIG1b47kDwPnG7OU05mNaBLe+Bgemk6qJdLJGqoA2oaX7w37drLs3wsSxbSBFYI4wmGtpIvg2+nSwYrRbDuaFMATu8uWTc6ttWCOjEHWUbs2osAf6T35UAjSHJqi5R+AEPJ8QidPP1oJwyPuy9Uj/wHw6Pe6E5aGURoV5oP1vQw5kiUI1rElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwcg3VL+yOt4gcM2fe22cNjf1MSkxJZUuujETHOEquZtCjLnESOgtLyOpsLezPCelDwe3k3R3LcL3aRvc10EriCA==" }, { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDxSx8U8f8AAAAAZs5OgNDkgdPINHldUBmBCwQPYYDyiqZWEW2hc0T7OBGLwMnIgZfTMA+uoPWvO59qTW6qhPEgHRbWE5a+pEis9uVw5RIJ3D5CwFU5kiGsdzOOsIYx3JbkEnFvoPIiQyMzL1sJ8hK6nT3TdbI+M1vL6v1aGV6S/Eww61+H4QNGe7QTRVeqglWk/xrB8J0T/ws+w59W4eOOsiNUfp4epGwXaiWELz/VGs5D0infIUlS/KeAs1lTIfcFxJ4p6MC1yX0Glca6vgT+Dk0A6CKveboqu7EiywagmkyK7zootA9PPfjO8c1QNrnExJqHr82AbMmAI6Y8LfCme8Fzra2Y4JvNLkU2X1GR6iBbKAbadiZV0+z3TtZmpnJX3WXASo5i4AFXoE1/b8tdZwrkyDIj0VugQxmT3kco3NKokEUztNbpg8vk9CAqnDgTE86/9/u/Z6779vj5lWtrLbXK+cAp0mJktEweAIpei1bBSXfGa4il+waJ3ODVudYhK6AIVC7dLxcxjp4LqXvMF+Au4/WzgdnoRv+0f5Y1qdlf9gH1al0ut3vBoge84iwty+UaBu3E0BBz2H1Alnm6IwdS6bJETURLsjAyF0kW41gFlygkE5jUw4qINbMmKOTNJklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwR+IJLuFq+Yl/PCvFq3MRH5FjM9J1/Zfp5ZzEJnRKeTvL6ga4FswIYtQQHp5L7XxzLyLwvrk0A/7FMyokaJv2BQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDxSx8U8f8AAAAACGNrCDAjNzqIkuYQ9xm9SayjrTaBVqS8bdthlQ7IPAC0zlc6jNzsytnuEknXPTl6o8FXGV1x6evdm9JLzmGMwZJz8HHrbB3Jiv1rewYTJY6JMgjsCtScqPctKnNKs77qOo9gS2OzhlJUwsap2AsCbr+bJhkQPlG1KA0JriyMXPMFYG+on6Ts9ZMr7OGM4ZQ1gGqYwYmbPOieH4zXb1r1Tzvw0wO8+xIHize0R2xLaa6pxpaVtsgfUUvCXFxzy8YQbOzfr5pPTkOIgmsssb6Fk2GbryUgB0decNFbR7C1wfaVVyynig2jdZtmwP3Zfi6pu96m+Ei5bDm55BMgUu2/HIJaAFUbnSEjk50DuB5XWuvqyT3289daK42npj5ol4NoJeMXZ/ZALvnzpZ/JZv82sNUYpvBIteauFH6zKTsZQQmvmcQ53Tb/uJ4GR9LKkuzhyZnBcpQ2x4Ehl+zBJAJZL5vx22+hyKsXn8TCjl9aMnoGEre7oE4lDakh9BIpSYuxA8ioknZE9A0dENyYlc3ZTjPpr36KM+3ZF6Z5wfnqt5bAd8dMOVF5y0tzuwOoaKJr0c0fbN1iOWXJermJXW1CfFLdZhEBUhYgfB+p1GKY5dbiFrXbtOtOFElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwsYDcSUfgdX37jVIMJLzx321ORNMNnLbQpfk1CP2LnUyHkDJn7L/Irl4eCCYyE9DlSIJt1UUObofgVRSHqo7dCw==" }, { "type": "Buffer", - "data": "base64:AQEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGGr4fUZl1pgE39zxdECo6aZn8DUMhhGxZh323sERm9KSM8jIkmZhQeiPyMESrhUcn+ZhLjB69EavAPUhGir30E2FiF382R4LMFJ3oDphCsSACV4aQKfMHf2eNmddpOhY1cKjnUXU/X9sBM24h0K9JLSLgfyVq7aNkxcwaGY/zZQY8XmTMcSEMTON4q0s1G8g8Qu/6NLOH3ZFSPx6dfjaE2f3fAOMzzSQWi8zXJER2xWtDcjytLZFgkqO+TdAuhwmEEPnqmWHM/8fB9ceo+LpGgAhYBcVKfdPXF9sR8KLSsR8yXbDwqeq/YVJS3DuViFiXQJWUHYWEhn8GdA3HSsGHYxpAGYxlQwwSFzvO4JfdLt+fcCYEI6cEWk1XHpyGGlEAgAAAOaOCPut8unV1Y9S5FH0A8uN58zx9Mm1Vem8AnX0k5QfFh67jViFW55+tvQUALdJy2jUPWXvS0jrwP0xPYwmwQS0VxBft9+Ko/GgQDUalPq2P7VjelLMxeCwnGGkxSMzC7Td0raWAwx2Q6XbHDZAxMhl0nu42q8Use5N7Ccp2c8GYTree4/E3WaYuwAWmkzYy6UL1AUxr4yBgZQB9fQ2npxe3QxouW5fpMbh7W6BrF10+hC5xf/mupDc0hh3qN7FnwogtkLRr0hr5OAvIzz4qJWqQvpjf3XqRqqztnqt5fvz1iidACesAaPp8kM3Dll+a7FSEZ9v7lX2V+ABdyTQPe32ACRrfW9NWhZxIExpAl166lEti9qY6BHE4WRR+N1qC4/SauFhWSjb8e5NbCy2z7EjoWfPmBN/eQJfZKedf5lg4a/E2gvDFk2T9ZbpAsY7b31D+Ei8d44+cfF8/uZWamml9WrFAAFEH4P/7Wsvpy6JyYprFEcgCSFbl1DbBj3KkUXh/a9qCpZfKhqViaocb5Zuc5jVfQS7/3ccl7ilW150nIqyhD04ZnzLacb8YpwISGV21AoC6SF3OZn7hcEqmlVAE7TkF06kkAzut1GDN3i6PL8xRnEPZGlwiVC8lBoJCTPK8RrOwvkUAWkCd+lIBCvK76T2W8ew66N7Nk0rfzBAWPTC7jpYEFPLZWrX4f0lGccNoEJAto9ZyOq6pi+0dxtFVQT3Ro27xjwAyKpGjHYY1Hqe0xnISMly9Evzc4PA+rG7RUNbgXJPb+Bu4vZtUdRUwBkVcAnyn26uZqw/QTRjX1+I0H35eTMa829091oVxN6XTcLfNAIX/3uIJAG5oGQsf+b5Q1SGwbqcQAuHRzw7nYQ1ZQRtmM9ikD51sE3/npWOQ0UKon0B" + "data": "base64:AQEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQgzcr/89L7BWlG7YeUHXFWhEIEO26u+Eq/zvTvhOWoqjgPK8uHkmFsHI2tCKXZyjxf5n7fuQb+sJM4YpKr4CAlbJi+6ThhzpCR0tt1XPPGetUzQ9f1LdnTWpo249ORoYpYHkuvqqJXYvxR3c7XXO8Zlj58UzWkYyNEFqTau2w5kO4r1FXBlqw390IMn3WKNSRGRncVk6aZDXrK2efT9TJAjgxQESZgvNHdH5e8qa17GG6hVglbxgZlniws7UPqIWbMqkztSRJsEGHJH/uC7V0cdnBKjRvxZM59WSwXxMsUJS1TkBDUZlHoBlcKtuZ3OsPS4e8ABD9qmwkqslthqfLm0xgYWfVXv0bKs19uUbJm1J0kq4QpHnzRr1DX20ktEhAgAAALFi3netrDsa0N+6d9vbZGunB/mXamLGMy8uey/p8QtcbM9enziV+ZEdS5EsWKp1CZwUBBdKCuRvzjaEd54RHAK3gM0KYle0i3NF5YVjz8IoBwQ428RZ92i0iFOa2LuYCrLZcW1fVWiQOPFk5KuUgkul7odK0y+1ENCP6DA8MJdkbRJ7FZaDq/WurCpXdNAJTbgEyS97McqC7A208WbDrAGaCgAeLg30TKXm1cM9L9w/V4wLd46wbcRvESYGvvYbYxJUp57GHRdq2EzQtSKMcGeoIAlFgx1sEFnkzL0A8EGnoINlfVlqtL/ebGJ4tZ2e0a+mBr5Hsz4Ge/UWXsSnZW1Nm1pcw16EAH0CBCG0fIxg6mM6DrvUdlswE+vrjqfvlCFB0ulnO2hJq30QXeFAC7BpoeJnAL8m/aIMSJ54BiwKUHDO3LYX8xyE56Q5TTQqq59We4NYWzkSybkZMVf0mD+vwQtfuncYPftrkv0ihcJEbcxxSIeEjcfwlv3D7EcyorKWFRtFb/fvzzzxogavqIiZHGqOsd/HtW0GnOrV1h077dWFsXwflYWKaI4CEG0fcmpQN/JnaDOj2jjX9SAQbiVx+dym0K0vaCO1OFjPpQlwsQUwdhcz3RXuwoecqKOv9okM/bP5IV1BvPdppG2CiM+ZHvO+JCKtc8z4a3C46bzFbIIYNeh50Dwwp9SG2CeYREF0BPOJ2T6TzNhEt3ngZHkHMSgdWwxX6whJFpZpWpas7X8SgD728aQ/QcCZCnPU6gN1zV38MTw5TO+L2UYtrsXPaYV+iPHjLJz3ARKGhf2O22VD1T9/Ta0K1XvZvm4JKHQQAnr7JCaex4nudfj/apMHi9ZY3eJvst1RBg1ZQAjJo6tDvwv1d1VvHKzxU1dPvjkkFc3uYrAD" } ] -}` +} +` const MAINNET_GENESIS = `{ "header": { From 3fc793364b748fc3f2e2bdae50f5df64a50561f9 Mon Sep 17 00:00:00 2001 From: ygao76 <4500784+ygao76@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:02:31 -0700 Subject: [PATCH 16/19] Use yup schema (#3849) --- .../src/rpc/routes/wallet/getAccountTransaction.ts | 14 ++------------ .../rpc/routes/wallet/getAccountTransactions.ts | 12 ++---------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/ironfish/src/rpc/routes/wallet/getAccountTransaction.ts b/ironfish/src/rpc/routes/wallet/getAccountTransaction.ts index dcd390b530..dec6d2ce59 100644 --- a/ironfish/src/rpc/routes/wallet/getAccountTransaction.ts +++ b/ironfish/src/rpc/routes/wallet/getAccountTransaction.ts @@ -4,7 +4,7 @@ import * as yup from 'yup' import { TransactionStatus, TransactionType } from '../../../wallet' import { ApiNamespace, router } from '../router' -import { RpcAccountDecryptedNote, RpcSpend } from './types' +import { RpcAccountDecryptedNote, RpcSpend, RpcSpendSchema } from './types' import { getAccount, getAccountDecryptedNotes, @@ -96,17 +96,7 @@ export const GetAccountTransactionResponseSchema: yup.ObjectSchema Date: Tue, 25 Apr 2023 17:08:14 -0400 Subject: [PATCH 17/19] refactor(ironfish): Properly mark `notesEncrypted` as deprecated (#3850) --- ironfish/src/rpc/routes/chain/getTransaction.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ironfish/src/rpc/routes/chain/getTransaction.ts b/ironfish/src/rpc/routes/chain/getTransaction.ts index e50c6bf2cc..540999d118 100644 --- a/ironfish/src/rpc/routes/chain/getTransaction.ts +++ b/ironfish/src/rpc/routes/chain/getTransaction.ts @@ -29,7 +29,9 @@ export type GetTransactionResponse = { value: string }[] blockHash: string - // Deprecated: use `notes` instead + /** + * @deprecated Please use `notes` instead + */ notesEncrypted: string[] } From d824866b98ca954e45bd87e497e13ec2a5f18617 Mon Sep 17 00:00:00 2001 From: Daniel Cogan Date: Tue, 25 Apr 2023 14:10:58 -0700 Subject: [PATCH 18/19] Add RPC for validating address (#3842) * Add RPC for validating public address * add client function and tests --- ironfish/src/rpc/clients/client.ts | 13 +++++++ ironfish/src/rpc/routes/chain/index.ts | 1 + .../routes/chain/isValidPublicAddress.test.ts | 35 +++++++++++++++++ .../rpc/routes/chain/isValidPublicAddress.ts | 38 +++++++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 ironfish/src/rpc/routes/chain/isValidPublicAddress.test.ts create mode 100644 ironfish/src/rpc/routes/chain/isValidPublicAddress.ts diff --git a/ironfish/src/rpc/clients/client.ts b/ironfish/src/rpc/clients/client.ts index 16bc7705f6..021fdd67b2 100644 --- a/ironfish/src/rpc/clients/client.ts +++ b/ironfish/src/rpc/clients/client.ts @@ -117,6 +117,10 @@ import { UseAccountRequest, UseAccountResponse, } from '../routes' +import { + IsValidPublicAddressRequest, + IsValidPublicAddressResponse, +} from '../routes/chain/isValidPublicAddress' export abstract class RpcClient { readonly logger: Logger @@ -642,6 +646,15 @@ export abstract class RpcClient { params, ).waitForEnd() }, + + isValidPublicAddress: ( + params: IsValidPublicAddressRequest, + ): Promise> => { + return this.request( + `${ApiNamespace.chain}/isValidPublicAddress`, + params, + ).waitForEnd() + }, } config = { diff --git a/ironfish/src/rpc/routes/chain/index.ts b/ironfish/src/rpc/routes/chain/index.ts index c53fd72844..f97feff32f 100644 --- a/ironfish/src/rpc/routes/chain/index.ts +++ b/ironfish/src/rpc/routes/chain/index.ts @@ -18,4 +18,5 @@ export * from './getConsensusParameters' export * from './getAsset' export * from './getNetworkInfo' export * from './getNoteWitness' +export * from './isValidPublicAddress' export * from './types' diff --git a/ironfish/src/rpc/routes/chain/isValidPublicAddress.test.ts b/ironfish/src/rpc/routes/chain/isValidPublicAddress.test.ts new file mode 100644 index 0000000000..088fd7d573 --- /dev/null +++ b/ironfish/src/rpc/routes/chain/isValidPublicAddress.test.ts @@ -0,0 +1,35 @@ +/* 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 '../../../testUtilities/matchers' +import { generateKey } from '@ironfish/rust-nodejs' +import { createRouteTest } from '../../../testUtilities/routeTest' + +describe('Route chain.isValidPublicAddress', () => { + const routeTest = createRouteTest() + + it(`should return false if address is invalid`, async () => { + const address = Buffer.alloc(32, 'invalid_address') + + const response = await routeTest.client + .request('chain/isValidPublicAddress', { + address, + }) + .waitForEnd() + + expect(response.content).toStrictEqual({ valid: false }) + }) + + it(`should return true if address is valid`, async () => { + const address = generateKey().publicAddress + + const response = await routeTest.client + .request('chain/isValidPublicAddress', { + address, + }) + .waitForEnd() + + expect(response.content).toStrictEqual({ valid: true }) + }) +}) diff --git a/ironfish/src/rpc/routes/chain/isValidPublicAddress.ts b/ironfish/src/rpc/routes/chain/isValidPublicAddress.ts new file mode 100644 index 0000000000..5594a1cdc4 --- /dev/null +++ b/ironfish/src/rpc/routes/chain/isValidPublicAddress.ts @@ -0,0 +1,38 @@ +/* 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 { isValidPublicAddress } from '../../../wallet/validator' +import { ApiNamespace, router } from '../router' + +export type IsValidPublicAddressRequest = { + address: string +} + +export type IsValidPublicAddressResponse = { + valid: boolean +} + +export const IsValidPublicAddressRequestSchema: yup.ObjectSchema = + yup + .object({ + address: yup.string().defined(), + }) + .defined() + +export const IsValidPublicAddressResponseSchema: yup.ObjectSchema = + yup + .object({ + valid: yup.boolean().defined(), + }) + .defined() + +router.register( + `${ApiNamespace.chain}/isValidPublicAddress`, + IsValidPublicAddressRequestSchema, + (request): void => { + request.end({ + valid: isValidPublicAddress(request.data.address), + }) + }, +) From 4b9dff1780ca3102146eeec8a809cb2629198cd8 Mon Sep 17 00:00:00 2001 From: Hugh Cunningham <57735705+hughy@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:38:50 -0700 Subject: [PATCH 19/19] bumps versions for v1.1.0 (#3852) --- ironfish-cli/package.json | 4 ++-- ironfish/package.json | 2 +- simulator/package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ironfish-cli/package.json b/ironfish-cli/package.json index 1717ef2597..249ba25bf6 100644 --- a/ironfish-cli/package.json +++ b/ironfish-cli/package.json @@ -1,6 +1,6 @@ { "name": "ironfish", - "version": "1.0.1", + "version": "1.1.0", "description": "CLI for running and interacting with an Iron Fish node", "author": "Iron Fish (https://ironfish.network)", "main": "build/src/index.js", @@ -61,7 +61,7 @@ "@aws-sdk/client-secrets-manager": "3.276.0", "@aws-sdk/s3-request-presigner": "3.127.0", "@ironfish/rust-nodejs": "1.0.0", - "@ironfish/sdk": "1.0.1", + "@ironfish/sdk": "1.1.0", "@oclif/core": "1.23.1", "@oclif/plugin-help": "5.1.12", "@oclif/plugin-not-found": "2.3.1", diff --git a/ironfish/package.json b/ironfish/package.json index e4ad09bc11..d3971dd7cf 100644 --- a/ironfish/package.json +++ b/ironfish/package.json @@ -1,6 +1,6 @@ { "name": "@ironfish/sdk", - "version": "1.0.1", + "version": "1.1.0", "description": "SDK for running and interacting with an Iron Fish node", "author": "Iron Fish (https://ironfish.network)", "main": "build/src/index.js", diff --git a/simulator/package.json b/simulator/package.json index 0aec203d65..f838588d5e 100644 --- a/simulator/package.json +++ b/simulator/package.json @@ -56,7 +56,7 @@ "docs:open": "open docs/index.html" }, "dependencies": { - "@ironfish/sdk": "1.0.1", + "@ironfish/sdk": "1.1.0", "@oclif/core": "1.23.1", "@oclif/plugin-help": "5.1.12", "@oclif/plugin-not-found": "2.3.1",