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-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-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/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/ironfish/src/defaultNetworkDefinitions.ts b/ironfish/src/defaultNetworkDefinitions.ts index b87f964ebb..1f86060371 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", @@ -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": { @@ -184,11 +185,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/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/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')) } 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/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') 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/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/__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/__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/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/getTransaction.test.ts b/ironfish/src/rpc/routes/chain/getTransaction.test.ts index b7b57eb2c9..83bbb2ecf2 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', () => { @@ -19,9 +20,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 @@ -30,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(), @@ -37,8 +49,10 @@ describe('Route chain/getTransaction', () => { spendsCount: 0, signature: transaction.transactionSignature().toString('hex'), notesEncrypted, + spends, mints: [], burns: [], + notes, }) }) @@ -51,9 +65,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 @@ -63,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(), @@ -70,11 +95,35 @@ describe('Route chain/getTransaction', () => { spendsCount: 0, signature: transaction.transactionSignature().toString('hex'), notesEncrypted, + spends, mints: [], burns: [], + notes, }) }) + 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 c0d6da188e..540999d118 100644 --- a/ironfish/src/rpc/routes/chain/getTransaction.ts +++ b/ironfish/src/rpc/routes/chain/getTransaction.ts @@ -4,8 +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, RpcSpendSchema } from '../wallet/types' +import { RpcNote, RpcNoteSchema } from './types' export type GetTransactionRequest = { transactionHash: string; blockHash?: string } @@ -16,7 +18,8 @@ export type GetTransactionResponse = { notesCount: number spendsCount: number signature: string - notesEncrypted: string[] + spends: RpcSpend[] + notes: RpcNote[] mints: { assetId: string value: string @@ -25,7 +28,13 @@ export type GetTransactionResponse = { assetId: string value: string }[] + blockHash: string + /** + * @deprecated Please use `notes` instead + */ + notesEncrypted: string[] } + export const GetTransactionRequestSchema: yup.ObjectSchema = yup .object({ transactionHash: yup.string().defined(), @@ -42,6 +51,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: [], - 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 + 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/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 { + 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), + }) + }, +) 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/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__/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/__fixtures__/getAccountTransactions.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/getAccountTransactions.test.ts.fixture new file mode 100644 index 0000000000..f5aa7ae7c7 --- /dev/null +++ b/ironfish/src/rpc/routes/wallet/__fixtures__/getAccountTransactions.test.ts.fixture @@ -0,0 +1,278 @@ +{ + "Route wallet/getAccountTransactions streams the associated transaction for a hash": [ + { + "version": 2, + "id": "97d34fcb-6ba5-464c-b911-9ab7f69d04ba", + "name": "test", + "spendingKey": "d2b98d37dca9911ac860e17128198a9897e6f8b83edf38472d7293ffe2979bf0", + "viewKey": "c6686393eb4485cea51d881c850d0e9ace0e91e0d6f8057bf12e75b3ea83df49eea4f671fa878b4247ac7fb7502712eb3547f81d9a060ff52843e1c8173f7a4d", + "incomingViewKey": "acf7b1c3237f8133c4250429d101eb77187994d42a54164a61ea88769cbd8201", + "outgoingViewKey": "0efe6b3f030701efd7cd662be76c32c373b49ba0c401a544bf6022e56dd5a6d1", + "publicAddress": "96e4356325d89e966628f6fd9275ed7c5292fe30bb6b6f9059949908c00c85c1", + "createdAt": null + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", + "noteCommitment": { + "type": "Buffer", + "data": "base64:VrZbEHiKZhZ/N2nB8cL9BB4oHJuMH1pM7/xmKOaPqkA=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:iFtskpdS68uzLF9t0sevzzbWETFznjO9KAs1l2u51Hg=" + }, + "target": "883423532389192164791648750371459257913741948437809479060803100646309888", + "randomness": "0", + "timestamp": 1682373466616, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAamm8Q7ihiSUg61/X167mg8q6yBx8TkaqN+NIm4i4jbOlWt9nD7RDM0H5RAJmRBgPto4iRmYhSLe3dSW3u7nHc9Bk45ZXI4Kvpove3E2XsoyWLtyXkD4ro29Uguiznu1PPiAH72dCxgWoV3LS7tbbcgf3dKeTTAGSqLhZl9x9+PkZMSM2IKq4hyY9KTZPANLHXMOUbltfpbMGatxUtJcXwEc6pu5vQ4Xfa5YfzzLzdOuoIjD9ole5zD6OUnPPQnjsVYJiy6as6c+iq8MdJSUloW/Sey6neBsFXxMB3hmjhxDwaG31G89qd6jdWAxIrJ/eqoKX86CXY1Dl6aUL/KXqX5vTSFskBJ9thJnM9gIHLBbCUlSFCd49U2IckF/iDXQSy525TNSvxMEdbBqdEBiZ4YM90+VH97gX33Kn1F7r69SwHJilb0Bq6y1+dITr79d9IXJh8RuGsUHpzyEDJgIvoJmTpHtCcOrIVDq7AFwU4PQ87V/5L6KDvDPlPHy/c9BpUcU8V7R0+yLESgssui8sy8oyQ5vkJ69qIzbFtjyfInlx/UfComirpidNhH5PRpFflcmuO2vlhfuCeGIftdNkUVqxXHh0StpJShyYECJdmyd7WfdlK9fj1klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwwFi0HOuZZcGprBhmCjUmjFLBCxm/kdy/GM6YudfKbZUPcbWC0CceFu51KbFU/fFTQeltyS0ilL4vurxD5rFgAw==" + } + ] + } + ], + "Route wallet/getAccountTransactions throws a ValidationError for invalid sequences": [ + { + "version": 2, + "id": "c927bc7d-3d17-41f3-8cb6-db74856f40ef", + "name": "invalid-sequence", + "spendingKey": "af73ab02cb3556f31d94a0783b536c4b865023086fee55430b9b7449e1aed03a", + "viewKey": "8233ffd5b3e1e9694d3120b31a12f825a6d47084cbaf428ece5540e4bc40d866e29369bf9d14f08cec3c1da28321bb50a072cd549a7a4a7e93c1a6e88247b32c", + "incomingViewKey": "25b73475d54bcda9320bb4d93016c9dbb4a92b6a22f1fec284268f09f2de2102", + "outgoingViewKey": "1c72638f8de10ed5e749718f99b1742c0eb08c352061ab85a4cf5b6ef2d62964", + "publicAddress": "2203b2050769be9ee21762106c26cd55288c0ed9ca96cfb7305f4c1c653e4a00", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:rF8RhzuBJaIDpl1iWgb+C/hVfRci401k/yQye8QhFT4=" + }, + "sequence": 2 + } + } + ], + "Route wallet/getAccountTransactions streams back transactions for a given block sequence": [ + { + "version": 2, + "id": "d9ae515d-0c52-4051-96c0-37bfbc82f19d", + "name": "valid-sequence", + "spendingKey": "11386b85923e629af57a25c305fc1cc3730360ac0d63a71eae76b3974e3efa34", + "viewKey": "30210190bd76920390a4138f7093595627c5a3824aecf613bcf94caa95503df1b65eeee445211f268674040ee7e974f83c09dc295e73241fc8070bbdd0a5401f", + "incomingViewKey": "4018790be055256af864cd65e2e3c7f6095686b855d007b9bbe1cae804f95501", + "outgoingViewKey": "3bb910120482cc7fafc428a3c92acdd37bc6f43dfdd5d4144986c4b825df4e91", + "publicAddress": "fc8c7d8c9d5af3c9de5352ad8c089ecb355b956255d684cff23bb7a4d30bcbd2", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:rF8RhzuBJaIDpl1iWgb+C/hVfRci401k/yQye8QhFT4=" + }, + "sequence": 2 + } + }, + { + "type": "Buffer", + "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": "AC5F11873B8125A203A65D625A06FE0BF8557D1722E34D64FF24327BC421153E", + "noteCommitment": { + "type": "Buffer", + "data": "base64:ueFAA7qXhhzyN0lOBHYPHAekYhdhecJD6Ni6ARG/5Go=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:mHkrOw5xSGIL0zsjJQhJzZE/W7iMtPGkwpY3PgT3Dlc=" + }, + "target": "880842937844725196442695540779332307793253899902937591585455087694081134", + "randomness": "0", + "timestamp": 1682373467491, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "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: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" + } + ] + } + ], + "Route wallet/getAccountTransactions streams back all transactions by default": [ + { + "version": 2, + "id": "997f3cb2-dd15-4a12-ac36-aec97c35d4a9", + "name": "default-stream", + "spendingKey": "3e02bdf31572777c5ad0265d111fe4741c4d4519dc57363b5308149bd3afc4c2", + "viewKey": "05c91b5fb519a42ee6397c1194ee55aedf254bfe54a8752acc44160aa4182728c05a2c52b95117e50322a93e7bef083fbf4075a9166000c845b47f4069c290d4", + "incomingViewKey": "377db54ded668bb8be752501ee08f8b17988a608368f5e85f095fc13c1bba003", + "outgoingViewKey": "4276838d06bc7229b48e0084f376329911d4e15dcd28482b68341ef35b2f4ae2", + "publicAddress": "3e0bdf39c28a7ddc9450862d06c54098c3a839e79e49890a6a28a53d13801973", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:AUcan7CNP85S8zIbEcFbjDrRK2eDVu7Aje1EXXv/6Co=" + }, + "sequence": 3 + } + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "01471A9FB08D3FCE52F3321B11C15B8C3AD12B678356EEC08DED445D7BFFE82A", + "noteCommitment": { + "type": "Buffer", + "data": "base64:hy0MkWMBBDhX6+R3MGn0JbTfkKERwNppxdGtk6wP4lM=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:nTLdhzuZWaG5M4ewLOG2RbsNywrKnVIoPLFk6tOHzI0=" + }, + "target": "878277375889837647326843029495509009809390053592540685978895509768758568", + "randomness": "0", + "timestamp": 1682373467873, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 7, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "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": "E384728DEBF8A850C87341655F0727D5FF20CE6A27E93455F9D71209879D2CBE", + "noteCommitment": { + "type": "Buffer", + "data": "base64:uZDUHMxifx/lLYAMvi3vTmQnUPVDJsn3EutGP6X7RGc=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:88873jI612VcWrkD35ZVZ4UJflVo/fJ5UuBbPsuL8AU=" + }, + "target": "875726715553274711274586950997458160797358911132930209640137826778142618", + "randomness": "0", + "timestamp": 1682373468212, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 8, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "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==" + } + ] + } + ], + "Route wallet/getAccountTransactions optionally streams transactions with decrypted notes": [ + { + "version": 2, + "id": "5fe8f6d9-1ece-48eb-8442-9fe77e5321d1", + "name": "with-notes", + "spendingKey": "6790949d45e8641db05a4f12074da5e1710f22401dc33b923e634d2108d606a8", + "viewKey": "b72377d60eb6b21902d7e8b9370b8549df54a88993b47c42fb73c85dc1d4e66f16db624bda1a81e09a8f07acaf23953949887c363e3ae8aa02581d76d1d28562", + "incomingViewKey": "57999426399e805872f195b76e0e88117bad0c0e28c37bc3b6d59d5dff5b6b05", + "outgoingViewKey": "d94c3cafa1cadea3100b2026adafab0d01dd92e2073dbeb9bbe771d7fbb58c1a", + "publicAddress": "7c50f3ada423c06c62931421829968c5fbb5a18e7c968451870dae49d85a74a1", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:NqXFasdgU7qM7mFrnp+Ew/x8QzCVac2HsIIgZuvY8Pc=" + }, + "sequence": 5 + } + }, + { + "header": { + "sequence": 6, + "previousBlockHash": "36A5C56AC76053BA8CEE616B9E9F84C3FC7C43309569CD87B0822066EBD8F0F7", + "noteCommitment": { + "type": "Buffer", + "data": "base64:2lQqfdPeGwXaF6KKXcgUtP5f66JjFiNLv0oMZcvzl0A=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:he+LTdiYAMeqjkTTv1bdEtM7pInrGmk22PGDmPMs8Dg=" + }, + "target": "873190827380823143577845869093025366895436057143163037218399975928398962", + "randomness": "0", + "timestamp": 1682373468558, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 9, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "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: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" + } + ] +} \ No newline at end of file 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__/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/__fixtures__/getTransactions.test.ts.fixture b/ironfish/src/rpc/routes/wallet/__fixtures__/getTransactions.test.ts.fixture deleted file mode 100644 index 79e80688cc..0000000000 --- a/ironfish/src/rpc/routes/wallet/__fixtures__/getTransactions.test.ts.fixture +++ /dev/null @@ -1,229 +0,0 @@ -{ - "Route wallet/getAccountTransactions streams the associated transaction for a hash": [ - { - "version": 2, - "id": "9abbc636-7633-4f12-92a0-db782ca14a00", - "name": "test", - "spendingKey": "91b2b2dc63dd9c563006d3502fd3067c06585ccdb3cd2aa1edd27cd39650f53a", - "viewKey": "0e15d9c17f2fbb4a3ce023304aa25f5b712005bdc84ac4166610067679b7c60e2c4d38d4e0dc20827b667afd0b9db6bfbc28594d3f2a7f803238562e9ac9b54c", - "incomingViewKey": "e7454586ca0b39118f075c7ff1dc408a78e6bb0fceb333440641cb443d61b401", - "outgoingViewKey": "edc549ad5b2bd8ef315c62e685cf7801a518c3befaeddff937720c5770f45749", - "publicAddress": "576ef7bb001a83ca839a89b73cc0d029a1f02b841f03a9d300304704c321e34a", - "createdAt": null - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", - "noteCommitment": { - "type": "Buffer", - "data": "base64:lCy0WfbvvOjqArU2bJpHpPHOBTc27bZfI9KXGPNFwBM=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:z8sITqsd99wHpnmnadWg31hXlWuzyA7mFetlJi+QCzI=" - }, - "target": "883423532389192164791648750371459257913741948437809479060803100646309888", - "randomness": "0", - "timestamp": 1681340302510, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "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==" - } - ] - } - ], - "Route wallet/getAccountTransactions throws a ValidationError for invalid sequences": [ - { - "version": 2, - "id": "d82d2930-cef7-45b1-a70f-1621272a2e10", - "name": "invalid-sequence", - "spendingKey": "93565b388cefcb5cb10d0c948d1f42f55a61a5ce2ab5a85c43342955f57fe422", - "viewKey": "b6649866be67541d557fba2b5a8fe8af121c4928996334ab90508c01d6b5ba01a51e7f3a30f2afe21b7851e7fb631e5b74d409d6d0ab56765a8ecc9cdc2afd43", - "incomingViewKey": "31f3ac75b3a0388901513b6a66d4e3610ca7866e063f8d762c1f6d453f8efa00", - "outgoingViewKey": "7ba10559777ff04115cecd230d49168d23c66e0883967f080ce40f37b6cf58e6", - "publicAddress": "767ecb0c25374d48fbac222411930f9481846566420e81da5df20e6264a4c9bb", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:8+xJbmRRG5eKVFDJb1OK3GkwY5+ss4OK3PuvcW+S/Ew=" - }, - "sequence": 2 - } - } - ], - "Route wallet/getAccountTransactions streams back transactions for a given block sequence": [ - { - "version": 2, - "id": "2f5ac628-14bc-40f6-94f4-0bebbfe8b32b", - "name": "valid-sequence", - "spendingKey": "20f9321aa00962cf391926dfc78ddf00e09dbdd342e186001e45c4d00eecbde6", - "viewKey": "46c831ac28336fe020a82bbd309551496d453eea041eca8e0276ecfdc83ee0339600cf2bb3d18799fc33378d741e162f7f9b9ec1110ebf9507d5263c8b8f1505", - "incomingViewKey": "0946d77b13f10bca7a9f893cfd7526327ba5831c8f07ca089ea6028335ed7f06", - "outgoingViewKey": "f12add34a4c589006d5baa42dd08be7dec41b211ad2ac16df4d1252defba6ec9", - "publicAddress": "97948359e2da1fa21f7284b1355b6960fb1d981ae4c5b041fba34b80dbc81f0b", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:8+xJbmRRG5eKVFDJb1OK3GkwY5+ss4OK3PuvcW+S/Ew=" - }, - "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" - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "F3EC496E64511B978A5450C96F538ADC6930639FACB3838ADCFBAF716F92FC4C", - "noteCommitment": { - "type": "Buffer", - "data": "base64:auK1qpgrRNXOEsIbkT37cCSbZLaOJT8CA01gVY/uGAk=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:NAke0SckGq5AGFyyf9WLmKtOWtB9jwHBmWXpEWsvxuM=" - }, - "target": "881271989446208257911980828427057262643615932976441214377264856368067535", - "randomness": "0", - "timestamp": 1681340309006, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 6, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAUamz/La8HjUliETgc3uGc0AfjsS3uVV7F4DW/kZP5ayOJd0pTXMl34n6KxuDHHWJUprnGM6M4o9eClVL3MTdmfvCjbO6WY9tDkVDltJ4BXGSILtq7+mTbkSizjibGUaEpYXlGk7Vcc9KuECeQBjJ1K/b1g6usY5Y+KXbm3tblAsP7PbbTxuwwl82rhYKXMji23RGfOm3jSZrkrGJ0tcQMt7HxMvvz1jjfLW9VgolAfKoiLcOgj5ewtMJVEDkN4BOcokZUfztndy3mYJpQ0uo3qT+pLmIeAXCuKNl/jSOOwGbCwcmoAlmIZPVGYEoJRhY5RhzDAkDcIprjtrN7+3E5xG9m9V7p/6Gx3pG0t+j66KmU2Uml6AsIFIMacNT9SMS9hB6RYyHVlIWrUdZF1W4lHNzVTXUQfU/62zjMSZMWbq9TYhpD3I3wxf2wH/0svUwuE2goYYLkMr/nEOEP46kNq5ahlV9S+JHFWP052MB87lowQ7gX1/bZYGGjpDbAybwfZXw8eanBBURMVxtc/W6orWmqQh2nfwbIeYcJ4qPlLyhtZxk6or11NnCkJShQH8nHraReG4e0wuzAKE3ExKgsGYAlh3ZO90DI3BCbtVHfOcV8eWaXioBh0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwuakMNCwRjPB1q56pDZLYZfMoxZEqX6zmP7SgOGZYNmyUj8vyIVflIj4Ui2ZSSeUORA5t2k+1JU7k9jwZYV1mDg==" - }, - { - "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" - } - ] - } - ], - "Route wallet/getAccountTransactions streams back all transactions by default": [ - { - "version": 2, - "id": "7835dac4-6956-4084-b83b-f14fc950c1b1", - "name": "default-stream", - "spendingKey": "d537d977c50468f574e7a0fc0fe8895ed1fb1d22baec83a7e24e934ea75294c2", - "viewKey": "1d75cf5c1e60ba2ee43a7bb5edffc3fa52d0e4ec4c57a997f057301470521ec70fad9da80b29d5a581035c5ad49152f7d3969d33aee7b52ccb7077545e1c9cef", - "incomingViewKey": "2d85faa273c4937ecfac7c19e175f9a61a8eb8e5b32e6335a145949b4f0da704", - "outgoingViewKey": "68f6cb905f2c4c5a16b9566ff4a1c53d2c17ba2c91b4a8688502adc825cab444", - "publicAddress": "0a1679099ec7f9dcb45b273b8b2cf0a6f01c91d8a47eed973ebd8e795dd2c4c3", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:onBosh37HEJo5A/C1lysl1Oc5FLajnX8ljtH+2VRbLs=" - }, - "sequence": 3 - } - }, - { - "header": { - "sequence": 4, - "previousBlockHash": "A27068B21DFB1C4268E40FC2D65CAC97539CE452DA8E75FC963B47FB65516CBB", - "noteCommitment": { - "type": "Buffer", - "data": "base64:b1YCwKwjtfKcdEhWErMhcpSdbq6mnSLxQ/FyD7+BS00=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:THozjLGsLirG1KCXti9H+3KIEudGu+zoJTHdzWpekaU=" - }, - "target": "878703931196243590817531151413670986016194031277626912635514691657912894", - "randomness": "0", - "timestamp": 1681340311734, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 7, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA8fcWvUpYJr75AbC9L1ALkhUZKmgD3+8Ump42rq6UFbyke9paBz8L2mykSGFOC29d2cteYmLaETvPg7Elh3t2LKnKXzxrIhJhppA6nsivn6iU4HON3fwqGpJAOG+GLtGG4tjlv8DVSJdCQ3UfjmsPZcaQWpka41S5CTrpxYkJAM0Iejei9j6j7AvBxyBQzAZn3eta1zviiD1NtzOtMZuXPiQM9TJASXeDpH8sqn9q79erfetYP2F6oYe3MfXRbv3IJyO5PGiK2uYP5TGdk1y/1MyJjKoynCeacWEPENYobkbAWPWYMHzO9uhs67pxo/2bcgUfchzb3gzUn5yGtiy75inZhwrYAc81cUBI9yZ28R6AykRXxsVC1H65+bmgNLovfdaTkif26zfBPV2zyxLHySSSFMMlfunTw5wCb8L+VCsuhPf7LltTGpXt7XQ4eHcE1uspuypL6O/GK0ysDALS9JyTThYQM5xmJSYIQdI49UzrdwyvO3dElkWYnJ+OpJpR3p14iFjJb9fHhx1cCtA/BlsEGfekvNrFwiN5jB+rfq0jcehn+Vnd6f5wco58JxHskZrJPKZuPvEea3BKDCblWMl+7W/q91O2hDKkRpLrbFynOjOfVAHPyUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwfFNGyGmVsklhRUbHS7XLC0nJrPGN9kfNtGZPj+SNoYF12j+ZuTjYNqg081Z+Uflz7/yCJG1dEOQZHVlisZgBDQ==" - } - ] - }, - { - "header": { - "sequence": 5, - "previousBlockHash": "209D721CDFAB205814B2610B4ADC64CF3B9F6551AE18A13471AC7D0357B2FF97", - "noteCommitment": { - "type": "Buffer", - "data": "base64:fQ/dbzvAYNj3slxBiYm61P5grUN9Mln8Ki3LM+HdU2c=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:gi5L5VM3BEKwppkzS4NOSq/octpPVoNHb5QrBOA4JM4=" - }, - "target": "876150796287198815250991109327239012206946009879241555988631840253579976", - "randomness": "0", - "timestamp": 1681340314059, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 8, - "work": "0" - }, - "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==" - } - ] - } - ], - "Route wallet/getAccountTransactions optionally streams transactions with decrypted notes": [ - { - "version": 2, - "id": "6257d28f-2589-491a-8dff-82ba100ef05e", - "name": "with-notes", - "spendingKey": "b843622fea2334c35a2135429d3aa5596ded1bced783a319e7b92aff10550ef4", - "viewKey": "402baaae1524f08090cc17a4615ea3c96b8fbea5f323b6bd4c349ee688effd453c9a425b434b1013d5b8676f2c8b082bb41eba4377171b8c947f57b91bdeb7b9", - "incomingViewKey": "41de6a61741b8c2fa0383994faa15a0a4880fd361a3299c20406c0e3d02bc305", - "outgoingViewKey": "3866253b6bbbda7419ad9ffe0c3c20d1feda2861d8d5258618278378f5178ef8", - "publicAddress": "f8416b375b30b57d2de4c1fa9c8e2cf9ff99d218f04fe44b2ca2bbc371610263", - "createdAt": { - "hash": { - "type": "Buffer", - "data": "base64:psoOrmWPvH7bjorZzuBt1gEp/kEpyedaWdN3dKbPXfE=" - }, - "sequence": 5 - } - }, - { - "header": { - "sequence": 6, - "previousBlockHash": "A6CA0EAE658FBC7EDB8E8AD9CEE06DD60129FE4129C9E75A59D37774A6CF5DF1", - "noteCommitment": { - "type": "Buffer", - "data": "base64:lz+X23SkbOEC8hgO5SrpoE4/cv3vYBVrOo+cFbujExs=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:qU28i/czL3Oij4iyXcAQe4e0jSj8w7TBHK99t0v8lTw=" - }, - "target": "873612455013551691691596639672017653407698459874762826227196885622232086", - "randomness": "0", - "timestamp": 1681340316797, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 9, - "work": "0" - }, - "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==" - } - ] - } - ] -} \ No newline at end of file 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/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 notes: RpcAccountDecryptedNote[] + spends: RpcSpend[] } | null } @@ -90,10 +91,12 @@ 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, { @@ -134,6 +143,7 @@ router.register { const routeTest = createRouteTest(true) @@ -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/getAccountTransactions.ts similarity index 90% rename from ironfish/src/rpc/routes/wallet/getTransactions.ts rename to ironfish/src/rpc/routes/wallet/getAccountTransactions.ts index 156146def5..2990936ed8 100644 --- a/ironfish/src/rpc/routes/wallet/getTransactions.ts +++ b/ironfish/src/rpc/routes/wallet/getAccountTransactions.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, RpcSpendSchema } 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) @@ -187,6 +201,7 @@ const streamTransaction = async ( confirmations: options.confirmations, type, notes, + spends, } request.stream(serialized) 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/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/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/types.ts b/ironfish/src/rpc/routes/wallet/types.ts index 3a92eeea85..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 @@ -31,4 +33,19 @@ export type RpcAccountDecryptedNote = { sender: string owner: string spent: boolean + hash: string } + +export type RpcSpend = { + nullifier: string + commitment: string + size: number +} + +export const RpcSpendSchema: yup.ObjectSchema = yup + .object({ + nullifier: yup.string().defined(), + commitment: yup.string().defined(), + size: yup.number().defined(), + }) + .defined() 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/wallet/utils.ts b/ironfish/src/rpc/routes/wallet/utils.ts index dda7a5ea92..01e36f7cab 100644 --- a/ironfish/src/rpc/routes/wallet/utils.ts +++ b/ironfish/src/rpc/routes/wallet/utils.ts @@ -104,6 +104,7 @@ export async function getAccountDecryptedNotes( assetName: asset?.name.toString('hex') || '', sender: note.sender(), spent: spent, + hash: note.hash().toString('hex'), }) } 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/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/wallet/__fixtures__/wallet.test.ts.fixture b/ironfish/src/wallet/__fixtures__/wallet.test.ts.fixture index f19f08073c..7f06eae115 100644 --- a/ironfish/src/wallet/__fixtures__/wallet.test.ts.fixture +++ b/ironfish/src/wallet/__fixtures__/wallet.test.ts.fixture @@ -2648,114 +2648,6 @@ ] } ], - "Accounts createSpendsForAsset returns spendable notes for a provided asset identifier": [ - { - "version": 2, - "id": "6d1414c0-c799-4f82-8839-8f64bc884492", - "name": "test", - "spendingKey": "036829ae0d9b6aac376d9f59fd7609625358f423498890bf8c888378160adc8b", - "viewKey": "fd72b4d1e49ad7156577a8cfb88a84b99e8fcc369a118bf99476e4dfea84e5885d5c633fff2b29ab115d80cdbb35b7ef4355405d5dabd0fc466b04432975cadf", - "incomingViewKey": "670f4ffb5b78379a60306abeff200139e3fb595a6882504354e2e6830c1e3205", - "outgoingViewKey": "84563b77edfee713eca25edf6fe731d0caada29a4e8ff8740a2e247eac8d6171", - "publicAddress": "44640c1effcd1923f339eef7ef52ed5113ebe01dcda7fb3e0ed9707b1ca87ed3", - "createdAt": null - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", - "noteCommitment": { - "type": "Buffer", - "data": "base64:IMG5fqswOI32eyG+aw7Y7j7NuXanZYFY1+GOMn2TOAs=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:fDynLOvmLCHtdgD3S5GCqqSf0/kyJcnDsATx2pjtpIs=" - }, - "target": "883423532389192164791648750371459257913741948437809479060803100646309888", - "randomness": "0", - "timestamp": 1681340271835, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA7aCwZtNh37HvZdcz53wO7MUi5zO4vd1WyEeDwY2BLMKr1079UTx9gaNGJoLg96d0rQJFR/NzSweL6CvQ6a3BtGbyCwcZ3Qpmt2CPxtfyxq2g/+9Dvdv3RcHBWTnpB4GTxcNQUFXumAOzvxVTRD1K/EKqwiwuvyHYUqgQin4ZkEoAj4tEcHV9ySrjec+h52vlBW5nUAUFjNFbJZnXNyPApl9xkwuc2mx86hFLm2+4QweyW4xo6Prg/q939OHTbmJ6D8+rE+012bwKFm7tmCOwJg1EB4Y2vQkzITZ8Y8EfTRkjraHAkHpq6vEVom5hZw+8mOEtJTfZDTqKOLguF8VYFMtSSUUkeRLNHDEg1C0omhpyRRKrWjyVHkGdAZyeHWZcRYSz5JGI2ZIHR6bncq5IrOkHpLDMktjwBzoRWku5H9x9Hy4WMltyoOUd2vwCRuf6p+6RntuzrCCBF0v2EoBl2nEV9LqWr2jxvnVi0O53ea5W5GUpMn/m6dQcaZRhxBwvFnADUdOo2ENPXXu8A33K+kcoaBPXDnV5nMbdkXqLofVzSIy8Pl25+OgiDW2VJUVxZM7e8Cn+wMvR+isBDWmfyIxMD/5tDiKY15xG7jnTB68EkuCsWa2bLElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwWi0UA6SkidG5N8YeBltZlefyTWwaHifowfLNM2Q0mA/Lj/5zZv87diDYveIU8ngTep1Nur5BS3saHVQsYDjtBw==" - } - ] - }, - { - "header": { - "sequence": 3, - "previousBlockHash": "E2404A5DADEFC3224F089806CB701701A644083215B23407950DD38A800E3CB1", - "noteCommitment": { - "type": "Buffer", - "data": "base64:HiA4HkfZ5kvpDw9XRxX4pHB+syKCBhjR3Vh+HpfRIg8=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:yw5pkZ+stCTEsaWN7fjhosUuAJmV33HSeBTxKsOJfDY=" - }, - "target": "881271989446208257911980828427057262643615932976441214377264856368067535", - "randomness": "0", - "timestamp": 1681340279127, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 7, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA7KQdXfWtRQX0nRvdKaeIEDcvIMGxtW8r4GDFbFxUFVKQ5kNjAampvZjVMAT9GWtKYvKkw0FARy47P+57HyhsAPY+Xt0wqpkx3av6y6MsJ+yhSanL9H5sWxc9Nbw7WiC4pv/bCRnEnV8Z17ToBqo4+XPE+Wmctary0b9G71pKQtgCaiibOm47F+5hA8v7aK5zVlp/U86+kmX3oeCfpxH8Ld4l6fckvox3r2M46uKtCRugqBsJI21AVKm7SS0CQwzK3UhQ45qm6m4c9m+erarSGGSyfFK4CKfBr+uZqBcINEWBEfyw6OTKWS6P2KImk1DtmCvONp8TqRWNBs5i4WnE2eQLgmuQ0PL0y5OdSpmHeuJH4MtYykDOONDxwBOk3IVycShCOF+2BTH/H8L5D/RjrV6M3XKYOVj8tgovhX8ECeGI4dbVkJU1nXw6rnbLjjvoKvvJfHxQZ2cRFtlPbMcd8IrEsOv44w1qzukuj0YpQbDlNWk5dIL34PNfQalmjY9LQ2k8HrenvxWfflvDYdokhKc9wOvc18OFQk+jGx0fNxRNewdIJSRMCyb6tHg+56/WdNFPUoX3X2RjCGp+h+XJF1OavKPF9zOqi76YWWkjt5iHDMffXUZ5WUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwHJkgwUbwAJQb3/i27ZZ/w+nLzrc/ftB7sBXEWlbfdFUly5QzFKzPYjyv+QO3rqLZXE5uJH1bC0yHqt8wYTWsBA==" - }, - { - "type": "Buffer", - "data": "base64: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" - } - ] - } - ], - "Accounts createSpendsForAsset should return spendable notes dependant on confirmations": [ - { - "version": 2, - "id": "25733123-6fb6-4685-b1c5-fefd8603fad5", - "name": "test", - "spendingKey": "65094cd913d32a39c5c2b0a95ba0271c8beba03aaa968904e5710ca259b1431c", - "viewKey": "9d567c80837d8c509ee184cb778c7ea5871f4491b92c5669bdd10caa3803ae9553f09060750e8480242d3375bc90f652dcee69307e14aea24b3603c36190d1e6", - "incomingViewKey": "2495fe7dbfd989220a1c2dfa624cdd54f4efde9a1c05b112d25140a74bb19a03", - "outgoingViewKey": "ad14b8f3457c93da276160f7f44c8d7f3b4ccf39195b4ec1f7cdaadcfdb31d73", - "publicAddress": "9ece2624db554667e3ef0aef34d5603590e4e980f6c9cc1e78a649c2649ea9a9", - "createdAt": null - }, - { - "header": { - "sequence": 2, - "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", - "noteCommitment": { - "type": "Buffer", - "data": "base64:l3ceWRn6g/9ZRh6vFGkvnCG8z7eCLI1MfyGK6jexI0M=" - }, - "transactionCommitment": { - "type": "Buffer", - "data": "base64:78tQVS3zptoQ0YUEiCTLYUsuRYSLJAlTvlaRCgxqITY=" - }, - "target": "883423532389192164791648750371459257913741948437809479060803100646309888", - "randomness": "0", - "timestamp": 1681340280704, - "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", - "noteSize": 4, - "work": "0" - }, - "transactions": [ - { - "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAseX3iKQNyek+fybEogBtxatiZZaRAJxMDh0K2DCm2ICT6mjr8nDdnR6WA4DOFwYdD5S1Rg/31WzSk2mQ8vCxrq7o/jQ3z2wRZtUwjJWKl92sQd+3PdbSP55q1rFuqyX6UP+ni2n+4Ow8gWBSz1gPIMBQOeVk4hqYMNAgzSYgZbIMRxCKXXR+BU/4C+kTXFfi1Qt+MeVP7WLUmpPnWXSEBTXmOw3SxgmJ7zcM232uC/SF10QgW0hoImogoYn3EhbWHHNkGkWrx8yJSII5CKBd0/zaK4I4iavj/2zNE+hAU0VgzwR77DqtKeLHcEBZesirY9OiCAc5E6HqA1wog59eytAIsQXJpS+tNRX0ZK+bijMtBHCHfu8cXsLFQrusr9sQEqy6eoNesET6i+JZjHjxCc/d8NtbXeqU+JcBVIktl8fF732cIetBYQKm/v8DI8UZnKPrhMC+ohBEhKZh/3UMlJYaQJa1XfANU+79sXld0sRxU3p/DGLvXLTyt7npZAa1IzfEE7xBobXZC+bJIFww1y2p4J69HwOKMSrDk6jnn7SzzKufFxhSQa5CCwzI3PqueD8ybIefeHbb8nsXLHoy/1lOYbQpk4R5ccU2p4RpB4HmoN5TIkgW6klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw21FptLxMfDfd/tbC+5bXLViDQpKvJDUzjEBJ4bS9ilbrDWAWCVhG7heeiYf5gqoVsR40c3ayupxyKtUt6lQrBw==" - } - ] - } - ], "Accounts addPendingTransaction should not decrypt notes for accounts that have already seen the transaction": [ { "version": 2, @@ -5735,5 +5627,343 @@ } ] } + ], + "Accounts createTransaction should create transaction with a list of note hashes to spend": [ + { + "version": 2, + "id": "1f64171b-d8f7-4e1b-b34b-34a444689b4e", + "name": "a", + "spendingKey": "b39870883acc995fd702160cc2f4c9ce4020a2437f8291a1a8329e8df2d016b9", + "viewKey": "7368e95fcee71589770d815fe5738c621c918d569602702f7aad1f49e5a681dc6d6e208739d0aeab3cbd551955a53faf0e0cca74738df6c781bce918884e9ed2", + "incomingViewKey": "a28b73034067f0c211daff0bbb9d1fc8a1fda310aca2b8f93709c9d338b2ba06", + "outgoingViewKey": "c0f01d3a079b495f64d65b0c6e0e8e28ef9708bcb52ef94c0e6b8d3d5f51bb55", + "publicAddress": "c1b1a5b0edb80c43e614556ab3af3fc242831c381dce3bd9c8bf2baaf560cc31", + "createdAt": null + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", + "noteCommitment": { + "type": "Buffer", + "data": "base64:jR6xwk1BFFUHQJrjs5Eijsx7KJcA0MCihG9uvC+ULRI=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:8xOvqNm45iOLgYakTlZY0T4GZwTwWit7kogqrg3beSc=" + }, + "target": "883423532389192164791648750371459257913741948437809479060803100646309888", + "randomness": "0", + "timestamp": 1682263213631, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAF4TsUxpW8jM0v2VFjmWM94Zu/tD12V+e0ys+i5ZEpreYERzA2kG2t/9kmH2s/g4Y+rxBCkKPl9ryVsg4DaQOjgOHekd+XHhGJTMc2S02axOJ2IqHiHbwhi4HqM5a/INjHUsRYMcrjibcz3T4Jb8kNrwrwBGvUoXHXT11rbftlesQ8rWeo7lDinvzLKAkqtgdLhtC6+YbwXBrj7ofjh4+rgslKlUXtLp0o3dl3dVqRHe0qw/tEzLJ5E6Qbwmqk7imFT2OlsNyMPw7SRuXLzy0tthqHDY+mYjkWqAnOPneAP5uUKulN1oGzq+HcL4KgoakHxGQ2cgfwUxij3M6fpTn046g/O8Pu93l4BTjXZ+qd4+FostWjQn7lmwqWJcYkcI/B8lL3AECzdkPaf+D8kTw1Xjgq+UO4Bq6cxBqFlxepO5yxHQs+BjDNbYVPEfDAimksB1h86T3uZem4AR1vrSWFBFFaNFDoRkXe73WtSBFxe9I7uJO8vMWQHVBvufB53rowUAfwrKL6/54HoM0uQWMCssg61HTZZ8Ye+vX9cU3uQPqaJk2RN2zaTdS2TyAja+TaQ6YkCvw9wtDYi718NEhHVP5BkOTemahF6apBV5cUFYYNaDS5QLig0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw4gKO7KkfeMeejPv55n1nwAKd7pMzehgv8watSoqwzwq0psfAVJ37QR4WBtykws6ydDpzzup/g5b+v6AXdZgMAg==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "31685A27EF28839797FE0792841588981BA21638FCFDFD900C657F4C75C999DE", + "noteCommitment": { + "type": "Buffer", + "data": "base64:z7Ux4Ck0fLYJJCXi3o97rJuPkS0oE3ybzVC0XzftwAI=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:uk5f+WHfexVFFhCgzL03a+at0gYKL8HXLV0ZfsQWgb8=" + }, + "target": "880842937844725196442695540779332307793253899902937591585455087694081134", + "randomness": "0", + "timestamp": 1682263214240, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAASLSsSeCZ/onS1rLRtIhNhbYBzqFjSwH8jJ4ihHaaTZGO9J7e0ZZtrFKztBRLFquPS2oGx84OGnHj8h/82pCAIGG4+XmIdeDJBWGY0hGNHcSPIHk5uPz/HUVAN08/rA+7MvsUXLlkNbA2dx45c1in8AIccFXP3gnM66R/ssJST00LaHJZrrFWQbqXCXpGHPlOVCyCgkh1mIemDAtTX7yp6BobYWSLBNoP/d6wdKCM5BClLtc4fJUzgr67VucIJGvzESLEonWan9hgVXnKpZ20konkUqF/SqUSM5MxMLjt9OmV/n4TfvXysyIzSsoxwSdT6s+6bRXnz1Wphlng3OCMQBrY5zpn08AMWhBenUvrMh1DsmGhf8X146sOnY088JIJHvi7jJDPzWQS4HOh1E9FTg31DpRoQHmdPlIkmo6VxuXnA8W+L+IcP62cMigrrR/DF6woPhKgwFJwGig40SpwWu6ytAnSvWwkufza64NxZH3QaxcIIqF33oRw0siE2LuO3yvrPgwbLF65Hh+c5R9sxxEtrYQp2lL4NFVSX24qjz2pPMlmGF7wqzTaXCYBdMOrBm12Tq6pbj5Ii1W5KbpUKH5JahV9VFD9OEz+zUsuTPXPEQxTp+Ao7klyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwzVd5h5sDpi9x9b7yXWBdKfMfiB75CbNuXCJI0ALkqlukrPgOoYjILd2y4b6CzJXFmfoMsVpLv3P+0zaoEpX6AQ==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "BEFF0B7F242301474594DB357F6D42C07B524DFF0F09177DC2EF24EF4F9B6211", + "noteCommitment": { + "type": "Buffer", + "data": "base64:sBPT54iivIdNWq5pvwo/PKdf7QkY/lOjNX6P5p59pRc=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:M/JvtNPHAaHce1o9lKnK4ok9m/hxfTZMui0QPCYbjkY=" + }, + "target": "878277375889837647326843029495509009809390053592540685978895509768758568", + "randomness": "0", + "timestamp": 1682263214835, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAuaR0n6aNzMEX9IarA6LBAuHi1H04T7fcerehm8zT2wOMee0QGFPNm3e2m/rGYZZja5hchESvM3zf+/BbCgfJZKMEYr5ZjU95ppwaQUNquPm3OuycPF2L01acTtxWtN9ZGHY7NtghMuomoLBHN4KyByhXxJJt2tlP1ydULI1KMh8WFj5mmSHLPjc9hn4TwUiCdW1Yw4umISWORsoGwC0DoszokHzdTdF5HnAmxvqrG6GhEotfYoU2NAE4e+EtGBaDrXYATJoyzTnrAd452m3k2BU99VfgluJx8Khl5Zq87qw10pwDQ9AbImj0EgjYKU9G9nPvUB5s5+en9sa1rj8bZk40HyBUo/Tl5dwy0Nkx0RCRgLtDgB6nrcHvvOd+01VYJU9E8wPUfvsZLlaAxsJmo+Av6nQMk1kWM3E8sH09w9vs+eqhOCtZmX1hmdqESLGS5I70Hh6mf5nViSyQLhdbfe4ExLu7iqLJGPBLGEcQG1D/Az7HIrUi92bYovs3mQd5r+Fc0UvMzCehSsWIEwlAnnm8ciaxoL9IrdkQIrM49lv/T0IlgFmf4Dp+u8/JYFinCYXOrme1qf+szIpbpgkD+NggntQoGXu16rE8hZfZj30iUx8AOc0DaUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwSUWz7cst4f4qM2NxUApBRFOAn47C6uemF0CqmbJ6aAi+8+E8GOAM07IzIvgv7md9EbKjxpceBnuYzQDRMgpjBA==" + } + ] + } + ], + "Accounts createTransaction should create transaction with a list of multiple note hashes to spend": [ + { + "version": 2, + "id": "c02b87db-d204-49b9-b5c1-c1a0b023d905", + "name": "a", + "spendingKey": "21f7cba0cf9b4fff569f09b3ee4ff3dc2c5115847ce1288f2eb0139dab49b945", + "viewKey": "ce2cb4e71e998803759318e9d3d170dce388ba29d06165691f6f3ee61319b0e549ccbd51d806bb40ac11dd8ccebfef0c206823361933dae1f1f42ebc87ae81d1", + "incomingViewKey": "5b262d940b1167885eb70b0686dd56df2edb7b544e22e615e346bd6feab10b02", + "outgoingViewKey": "f5ccba407b41db37f092b3bf169e89869cf44137b096ff5f772510720fa9a259", + "publicAddress": "46fed399cc809e1bf7945e607f2ae6f16062b5af5dde8541502a103ff6bf1b66", + "createdAt": null + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", + "noteCommitment": { + "type": "Buffer", + "data": "base64:57Xyflfs2Ye4Uc7FFE1DIsSWB2wFFXiSkJTHNKPHfSQ=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:f73fpKT92anUIp8zMXNZGljb25ePCaUOK8T69uJyGEg=" + }, + "target": "883423532389192164791648750371459257913741948437809479060803100646309888", + "randomness": "0", + "timestamp": 1682348446470, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAf1BDdjUCk12qERMmKsFcEpMCdxCd6qvVcD4T6HzccN6FVb/pkVfsVTRrWlKsekJ1jFAoibfb7Ky6LqWlOfbeEw6uUpvnXUuu79RXnbeHEh+Ca5PcRt2CNM0gcev61PRt5MU8ts2zQ69tngWeph5AKJbXxC3vOkDJBxWb3HnU4RcTd7T/r1yLPCzZTOmVahA/dIHpBtwn1gRdahVmcH6ArZIl/OExmzxdsCv7iAUqkLiuUd9aWCID9rTvSn02V9Ote17dHdVq+ZOD/flp9OmfnLO67ZW5knPKIVtfv9+FYMh3c3vZi5BLKdRwdsfH01H7Y5wm7xjbGBL91S3STgyH27xJt7KadLMfeMHIKdOZ37+5e8dSNfNAiIhWaZR7S79zGrTD8YjqNuKwyD+LVbhxciTCXkgCV/yldbnae8JGLDpS75ajZVxsvNP6dF23vNvpFKnqqSGUFy+zFY+gg9KV42MkUt1DVBE0QW2uwTX7VfywCKvc7SX8onDjdKZfhok2HEWC7YbCUs3TFguRnS4ERbzxgYY1uhm2ugo8dkiq4b+MIUPJ5kCY1MgnQ54+gGbsB9aJbFlkrICFuh8EWzlP8P7QCg01fjvmP22/7OA4FQ5OODJFkRXb1Ulyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw6+1Th0GfyX+AqJyR3uyg6uQ7BGz/npH4DlFwKKl4g+qA7vGVqPLpOoID8N0F5GDj7GFrp1I9rScqVqq9RdKpCw==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "78C050F3D6022D90880A9E291D1959A1F78A48C08476DA4C35F1B046B40D5DAE", + "noteCommitment": { + "type": "Buffer", + "data": "base64:5uE5vLeUaEC1bqEMRlqrvx/LuLLADVR4+tgoV78yGwU=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:ukTVFJy727Ay1ltuewVVA0eTb9fOkNIdgV1y5OcW/A0=" + }, + "target": "880842937844725196442695540779332307793253899902937591585455087694081134", + "randomness": "0", + "timestamp": 1682348447099, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAVEI3MbrhAxKMixwPxmXV1Dx1HelLKbynqeRVQELRFhiX6fz696vuuI8EQr77hUL3FUT5vlXfjK6fAt+xaabS1G3a68032lB3ERzsOiMjEK+EQBSw5RuoT8Zx6OyIXfOPIjL9YRcmFHOQT9UX4UgiIiFpkICK7bf8U1TXqvK2oS8D5cz/i2ri/xH0eAtKnH6D/2uZfiExA++zHwCXk+zKQThzy1hJdr9u0KT6puH5xzS0MTXYVtkIEcVoQzmUoxgG850/VXozH0g4dIYIXrAC+Dc6PYGtm/QUb2CxaVJoq/Tkde/t52pUTosM8KHMg20vXIHYEmjJr9X7y5+cZ7s4nE1vutyaH+7YKiWnP1oetB9gKhad+NrZ8mDqfhVcCDYFKpupB6CXYpwYlyEjdnbds+jw7Trn/zl/uEPCLX2fzso+ZlpvaiuHHtowhIZpaFXfdm3XR0Z/otjRm2fNHlAT6AH5XIxVuC9CPuhah4jAdoFEaUAepJaXnNajBI6X13MvXM223Y4fae7SS8/oViszeljpSjixNGU3hqTtYFNGeHSfiqFhUy9Xaar4c83cELc97ZQmpnmnCLA00E68PmshfH9ptj/B1ZAdgZ3WwtlWv9VJjMzb+lfoj0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwDXprI1F3scxgE4SpGLfDpP2T81CTi8S1+uAnQl5T9ds4Ir8xoB0vJsEsm4kjV6erzLMEHvZlH5oe+PNShV0+Ag==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "EADF41C63707AB499CA38DD7351A464D17F984C0AAE55CC006A274C86925A076", + "noteCommitment": { + "type": "Buffer", + "data": "base64:YEsqX6u4246urDqjopkGx5ISaBU/JctIpU6kGH8Jg2Y=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:zjvpIFHYDUibO5XtamkwbzIVjJeSaicp8P/v3CnPSEY=" + }, + "target": "878277375889837647326843029495509009809390053592540685978895509768758568", + "randomness": "0", + "timestamp": 1682348447737, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA5+Akq+WsG643yZMominJ2jCMneg798JX3fD2RFKjQUCXAmTHB70is1BosynhSdzuxxV6nNiTj2uCMbOoljKR2+lAkEKWryRkTWCG5HAlXnG39qyS4rxo7qiKIdDVxON4J9WMbls6EY4mzETQScnTTIJhuc5piLFRTerzATuWvZQIiDmSnxRQmHBUwpVRAHDHUsMEGr7RTqP2HRmbJGNtRhm8Jm2iYoJMxl3fu03kQxqsIFycYmNwPJhJzZoHq8Sp1+aEZN0qDaAWk95IwLZjdJ2OKee9naEvlRV3VxuFEfnSlIngdKFcMd3rJCxXpWJPpg9HaJ4YlLNoi95mBJ1cT9Bt30DGlhBN22caGkBPTqStcInGm7BXGBpyOWOVp1ZaeAuLKFDWAbTJ9DkLMWLwxRkgO1S9FhMCwEdVHMFwsfDP6u5TyBMmbABgCC0Xj2aMBPHCBbfkdkf+r/UmJtF2tzhi7j7AXBLmDkFU+1q7Pa8AGXPEAusFmlccqYiHml+pRhhEHvhEl1CYsB7ngkN/IJcIb8y6YNNVLO3tRh4It+Ub9iagEvCYU8BxC0OtT1G5fS4Y1lNxM2djDUlqqUPoX90/yl9+ZPic9sOVfFXd1dFDA8q8qmKWsUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw9JsHKMnTJaBt/YJUTx6hGIzu3UXFILwTEXPM1WGZLbuxtPlSxrS6dcrIPz9tsyMtIcZlE6//e9x9BTqg/iZnBw==" + } + ] + } + ], + "Accounts createTransaction should throw an error if the note hashes to spend have insufficient funds": [ + { + "version": 2, + "id": "25ccffc8-4674-42a0-b4a2-540d4734ca88", + "name": "a", + "spendingKey": "5654cb1295f84236286315ab064fb89c12309d068a8a8f72f14f09c0e779c0ee", + "viewKey": "4e552fbbd91f67e5b9bccb77aa7a696dee750d02cda52bad49c4d97929593e0281d7836c5f70b3d345b16379ba6893eff7e479175e4eb6f0a1394a8410cc0bac", + "incomingViewKey": "8692226955cd36fd579db43bd59c6d9815aaa0e00d394dde8ab0b3bf92b2b300", + "outgoingViewKey": "cde0ad7dfd55163b5444d2c5e275b506c8ab686d396ec4b7abad90d362b6ec2f", + "publicAddress": "c463042f62bb44616e08a8c79e6eba4431dd8b1bb0ff9938ee14a06719b927ec", + "createdAt": null + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", + "noteCommitment": { + "type": "Buffer", + "data": "base64:9WYvyfabJMvHPbi1z4Zc60O6K6rTyZemDDMEzd8SkiY=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:1jShqSfmsd8ZRv+hxcNR9VyYOQRS8lY1Chv1PI89QuE=" + }, + "target": "883423532389192164791648750371459257913741948437809479060803100646309888", + "randomness": "0", + "timestamp": 1682348720860, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAD2ZNdPmB8iAAN8c0FWMvMehvYrh9HkjEX5B/k3OBaLyt8hhh6gd+GxTUuZQWhs0XYg8C7zam6SUr9z2I2FW141bPNhQ1ComxEAcv/RH32VqS0LcOu4mEgjaEsaSl4QPeI7VKMimvWOShP8gkZuP+xt1rZLUpO8PzS98PrEB+LEUPV/ktUC6L5vQooF7Humh3SkdktSGGqTFi+lFn9Z9QTtat/ZKI+gP+BiG5qiHYDxa1yYbj6HRs/4Bskfgc41AtDSL4pxTpzkysiCU0WADGHf31wnzFW4dtPL+lXHRJ+TfOGgOTGoEHyzr6t9ptCNlpAlJ1Otmfbyi2L9ZdmPatjOyvu0RheRcmPEYJwpl0vT89d1i4zEpCG6PYDMtvxnc5VcEWXAmVr3X3uyb3cq7U1GDfwbLlUmYC5dVCT/tsTzzDlMUhTFAuWk1jLFQpzt8cJm1VeCqtPPxMxoqM/3uDfQLPiFNLOyL5YhRe2cNWSIIpwl4IaySFrRGYcG5cX7Og5sGuOiWemjThSntbfKN7p+g7S4b3fUkayf2lirfi5K1nFdMpbOMvWyhPvIuxjs1faugl4YobMhAebMogpZuXs3nXGpBfPjEyf/aQ1saCZc8JNz993ky7TUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwlFZ3Xaz/o1bLMM87R+rPe+iOtPm+7W5+0GPx7yJGkmCympNIayrCG4iGWcJ2ozhbZ13vB6Jg8BxpyaJ27EDVCQ==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "7F9DAB6D98F86B1C42CB55A5F9CDE94163A93076E39E1877B6DF299AC78F2794", + "noteCommitment": { + "type": "Buffer", + "data": "base64:eYDJPcQ47q3oZI+A745/iPZn2VuC9lAHH9klISr6Y18=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:RR+yLR+E7At3SzbNQ5p73hRiHkVC8IDYaiLgw1KGCpA=" + }, + "target": "880842937844725196442695540779332307793253899902937591585455087694081134", + "randomness": "0", + "timestamp": 1682348721496, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAOzvdM6J78XcIGAqPDr+yKGSvPpEDYhOglJKZPTI0YKmEXjU6ausvZ67il7c6azPw7TUiu2naMu617WlrUT9yYa1qo07i/YxkrK8TPgTGKbqCuI8QDn0+SmAnFl44JHp/mpGWr+QOuidPKlSfwL0gegslksVWrO2plCpSN8jW90oWl38gusCg4VgT5NlwXZfbx4x7YbKz9055MBKobORJW1vuOa50atuxH1wrUhZK/ki2I3DCDPnsRvKW3fDb0KsoXlPZsKMKagccF8Eb0zKeVKPfMyXYxM8zt39VdZ+E5EcxQLCMoKdKKgXEfMTxmZk3KIpVcp3g2SUImpVcQvSRjxFMVB3r+ZNgIV7OfVjngUiAUxgPWbiB6/tLPn1Qow8xezo6JGhxKE4EflQWxo3i7Gldvf/evPTS2MimZog3zQDn092W/09xdDlMl9LtAxHLdL6KlZX30DVWRb2WMdq+NI3bwXclRWZIXqA6WpsMna2DZDqOSz1NXHw7L9BA6vDVGi3Ivy0rBNZdKfihv49zKklVdVMBf+McX0UhyYPEjWVQs+LTSwgeGSorPsawGihm5jQvd4GoqynW+TYVrLnghDJdb7gruUqJLZn9x2WoaQYzsF4pxBTYxElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwt3F7aiK13910ohNvMlvNHQa+zxnEPNlr04E1UhWKNiiAOhP5sv3yqaAkaDPd8vJen9/+MRxDIL9hHxnui5LQAA==" + } + ] + } + ], + "Accounts createTransaction should partially fund a transaction if the note hashes to spend have insufficient funds": [ + { + "version": 2, + "id": "162836d0-81c4-498a-85f1-646a2579b829", + "name": "a", + "spendingKey": "49ec3862e95f5387ac093fc056a88612974558d10567f09b5d7801c501f943ff", + "viewKey": "b6db3bc71df360d46a7f4c9960fca0f834e194548963be64c4f2282f4ee129d1aaf92fab062046f41dcad55d60549f79f8461d8106078fd269ca328f35786560", + "incomingViewKey": "45e158567815cf2cddbf996a2ddbfd3744865b633b56ef0d4d7747af923b4300", + "outgoingViewKey": "7627b5e3c674a8a790670e6ae023764fc8584ab2f2e923e8fc24ccb06f96dc6e", + "publicAddress": "e81bdb6a865b1ed74a8c2bbe150e38f5587dde3c7a07023c5edf1ae4243e4fc7", + "createdAt": null + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA", + "noteCommitment": { + "type": "Buffer", + "data": "base64:owZRNInEquRExNmbcD2FsumAc7XogGjKBxqJ91ZxqlA=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:dN2QpNNnnfhsGnFva0xdqxDzyryUY6QoVYJ6edrRoag=" + }, + "target": "883423532389192164791648750371459257913741948437809479060803100646309888", + "randomness": "0", + "timestamp": 1682376526496, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAOSRVwSpwnqBpRp7R+KhNZh8AxTOeNS1I07rvGyWVj22HdZMlo/1eFzjycqAw262OwxBJd8LJA0Noq6LPagtngCj6f7HnWuuFex77xD7NKvuIcPOcZFi+PwhhoQxs/x+MfVmY4Np5AT7FyJ3ldGSupDvNNQJcny89F0V7P1rIzxQOuTrsGq2I1VSYi4bkIgncgGoXMv26OOy8WmOpJ4xqTkDitChRSzPoydctzsfIXTCTB24WvxaxELJDrx/7lDz5YiqIiGLk2+fh7nIygwZnmzPfuOsMr+OfksvgB4k14L9uEpx/hMO++SVeHt2Ir4AfmdvPs1+E/0gmlYzsXWSNAcDhHI9Jo6K7+5JhwnKHqunQiGt7HT9sFO4EQZc3t4odU4Lq1j8ZI+B4eXU8SMS0PonMGItULZORrpQJ2VvFhlfZEARt9DWLIWUW8dlVzoS8CMIMpuW5zMJi8UDaQ/bOcEob9aGgZDtnMLxBmxwbv7NqSCf5bR2oc0rPSQmrfJTrIdMYK9bgyv9mgvqa73gvfGO/ZUs/XVCIdtx6JyOLz+a4Q2vSbpFsuokfexU1r/ybhzGj+dw/NSYfdYim+o0LV3fuITa6YlkG3fyC2XEXniXXYnQVD4WOPUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwd2VrcrTx4ToN3eHhVT6OY8yktcpkVzBz2XD7yZQsdxPw1ywYsuVpzVxFUUd1UZRqeWlemWDoV/M8fG9ITYs3Bg==" + } + ] + }, + { + "header": { + "sequence": 3, + "previousBlockHash": "C2C4C74F49C9AF07E73B0EDD1FAAE869D6FBFDC82768958DD9E6392D0BA054A2", + "noteCommitment": { + "type": "Buffer", + "data": "base64:WbN8doXI6LeRQhQnG7j8stgJ3U/2zYeSWyjfgsXtxm8=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:x62xVfpOUOsFdgw9vmgfXlhiXh+c3mdx1SPqMvMr0Vs=" + }, + "target": "880842937844725196442695540779332307793253899902937591585455087694081134", + "randomness": "0", + "timestamp": 1682376527211, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 5, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAEt0OTyQx7r/iOdVwWABLS5ORtU71ugS2EchO3kaP22qNq9ZKdIDvi9k8omXLI3B+uKRuWbeVq1eeZ/1Srnlzbi7Z47sOx9wCO1NXQhwGBHuzn6s971WmhxdsKPnUeg5TP4OGOkE7EudG/236zk9HjFL94VwJsmhmM4FBfPU6WpEPV4ZndXyFPHVYFxxwMNlkh3Iu6g/OJY5XA027dI4wH22k1gv64MpbcO5VOlH3LP+SXGBaB6i5xyDPPa6BCK93e74BjjN+Hjmw/JG/Q+Dw6nq9Nk5QTaxOHrOgqgslbb5bbE+irDFvupr6vVDAGVM99H9xHLoOJ1D4ILWqfk0N2ztraY7ymdwwEvOtaZd4+FO/sjkClJUvHex7kZvqMfpjIZXsISLQUgGSHaeWvUi6OQ/WHJZ7j5uMXMyQ/Ozlxe59L007XfzUHJH4+BEMF49pG3w+p2AKZbwKtOWcVJUnVwKyNTrPZnr3fCnavH6qkrKNcwQrW6dUEE76pBY6RMmdScrpi1Ul0WoaIuT0MwU6FV/HfpkqVWEdIYmDCJDa/wW+fAcnp8GDUhuY4sPeyl6bIOffTOMhwMrJzYQPUB4Uj57PqAbw3whwdBeX3jG4MOGdoOAgA6ZSiUlyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwL0Grs0SfZc4jENFiGT/eUe81IyhSVcfg2cr92kxkxiLVn4VFN/0XRRIKmpiT0eBX8RDmEGqY2s63l74KVlPZDA==" + } + ] + }, + { + "header": { + "sequence": 4, + "previousBlockHash": "27AC2113092FB476905D801E9CB08459F008E06F84CCC7C6EA9CB8A89BAFB742", + "noteCommitment": { + "type": "Buffer", + "data": "base64:Qg72Ym0Kv/GHBssxaHZIhT9KIOVSnd4LKfSUJBXDEWg=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:6ycDlIWykEW4ebqczltLTfXlJd9KEvHUQP2JfjUmIH4=" + }, + "target": "878277375889837647326843029495509009809390053592540685978895509768758568", + "randomness": "0", + "timestamp": 1682376527899, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 6, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAABFCJjuvwsi+9IEDPZdb+RwkAVZTTZWclchYXcnySO/OAf58RDPP2XO1wXdlT5VlioD6lGFzBevlkwIocMQvOwsj1c38N+QoOMevhaFfkvTutCm39FXC04VW4VfwTNFo64TKqsdkmW0grxz4egJY0whYmTeTUKonD7yqIJh4S1bUJJ7apAJXTJfxD68LqT9uI4sPmPDb0aMKrNcRIXpQ7lxHc0GTBadJmF6bX6JI2eWGvF8WiUhm3AT8K9OLiNz1K4TDIASLNN8xN9ocDpr5xhDcC5Bw1/0vokAu1RYK1XF5jvKvYu0F+wQ0sJ6gDQc89Uko4HaK4AFtsGyPG/erK6Oo7fy93944fSyIVxrgEZnlAIhrs7eiKXz803dHmjHVsxAV55I1mr/9Bkg1bB5EfHFyAhn399cDxIy/nybfEb91NTGdi5vMMzJwMGHAqZVg2yUm6dS2buSkjoFAD1DLup+zPQ+vx6ysx0C2/V8EIVfh99OHRFGN96Z04tYdu7ldKcx6BDa8IAu9T8KYkGIhg+gdQlRaRK82bG1GJEDU8J/lZe44KkIEm/b24HgUVxRCy4TV8N/mxSwJeVbFYvbybDOuUpu8ncZaNBIdq1MGTdVHb1iAzw1nCxklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwquqnPZZDm04E1MoVCoaWtLrEBB/ZHRBwKu/TDX48Bex5LLx0LgOMHZbJcksthI/3BdIF9cglqIQekJT9+OweDg==" + } + ] + } ] } \ No newline at end of file diff --git a/ironfish/src/wallet/wallet.test.ts b/ironfish/src/wallet/wallet.test.ts index dd730c40ce..a9210998e1 100644 --- a/ironfish/src/wallet/wallet.test.ts +++ b/ironfish/src/wallet/wallet.test.ts @@ -2,15 +2,13 @@ * 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 } 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 { VerificationResultReason } from '../consensus' -import { Note } from '../primitives' import { createNodeTest, useAccountFixture, - useBlockFixture, useBlockWithTx, useBurnBlockFixture, useMinerBlockFixture, @@ -927,6 +925,121 @@ describe('Accounts', () => { 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 { 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' 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) } 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", 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...')