From 5f7a5eda6be2024a9bcaf6b1cbd54e4b9ebae196 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sun, 14 May 2023 21:29:12 +0200 Subject: [PATCH 01/79] Add support for indexing subscription create --- .../shared/database/schema/subscriptions.js | 30 ++++++++ .../subscription/create.js | 74 +++++++++++++++++++ .../subscription/index.js | 6 ++ 3 files changed, 110 insertions(+) create mode 100644 services/blockchain-indexer/shared/database/schema/subscriptions.js create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/index.js diff --git a/services/blockchain-indexer/shared/database/schema/subscriptions.js b/services/blockchain-indexer/shared/database/schema/subscriptions.js new file mode 100644 index 000000000..7fed48a96 --- /dev/null +++ b/services/blockchain-indexer/shared/database/schema/subscriptions.js @@ -0,0 +1,30 @@ +/* + * LiskHQ/lisk-service + * Copyright © 2022 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + * + */ +module.exports = { + tableName: 'subscriptions', + primaryKey: 'address', + schema: { + address: { type: 'string' }, + publicKey: { type: 'string', null: true, defaultValue: null }, + price: { type: 'bigInteger', null: true, defaultValue: null }, + consumable: { type: 'bigInteger', null: true, defaultValue: null }, + streams: { type: 'bigInteger', null: true, defaultValue: null }, + // members: @todo enable this as an empty array + maxMembers: { type: 'integer', null: true, defaultValue: null }, + }, + indexes: {}, + purge: {}, +}; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js new file mode 100644 index 000000000..0f9ebf08a --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js @@ -0,0 +1,74 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); + +const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const accountsTableSchema = require('../../../database/schema/accounts'); +const subscriptionsTableSchema = require('../../../database/schema/subscriptions'); + +const getAccountsTable = () => getTableInstance( + accountsTableSchema.tableName, + accountsTableSchema, + MYSQL_ENDPOINT, +); + +const getSubscriptionsTable = () => getTableInstance( + subscriptionsTableSchema.tableName, + subscriptionsTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'create'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const accountsTable = await getAccountsTable(); + const subscriptionsTable = await getSubscriptionsTable(); + + const account = { + address: getLisk32AddressFromPublicKey(tx.senderPublicKey), + }; + + logger.trace(`Updating account index for the account with address ${account.address}.`); + await accountsTable.upsert(account, dbTrx); + logger.debug(`Updated account index for the account with address ${account.address}.`); + + logger.trace(`Indexing subscription with address ${account.address}.`); + await subscriptionsTable.upsert(account, dbTrx); + logger.debug(`Indexed subscription with address ${account.address}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => { + const accountsTable = await getAccountsTable(); + const subscriptionsTable = await getSubscriptionsTable(); + + // Remove the validator details from the table on transaction reversal + const account = { + address: getLisk32AddressFromPublicKey(tx.senderPublicKey), + publicKey: tx.senderPublicKey, + }; + + logger.trace(`Updating account index for the account with address ${account.address}.`); + await accountsTable.upsert(account, dbTrx); + logger.debug(`Updated account index for the account with address ${account.address}.`); + + logger.trace(`Remove subscription entry for address ${account.address}.`); + const subscriptionPK = account[subscriptionsTableSchema.primaryKey]; + await subscriptionsTable.deleteByPrimaryKey(subscriptionPK, dbTrx); + logger.debug(`Removed subscription entry for address ${account.address}.`); +}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/index.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/index.js new file mode 100644 index 000000000..7adb3a6ab --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/index.js @@ -0,0 +1,6 @@ +// Module specific constants +const MODULE_NAME = 'subscription'; + +module.exports = { + MODULE_NAME, +}; From c4dacc80a1179b3f89ab269e2805731de7ea6346 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sun, 14 May 2023 21:46:52 +0200 Subject: [PATCH 02/79] Add subscriptions endpoint to gateway WIP --- .../http-version3/methods/subscriptions.js | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 services/gateway/apis/http-version3/methods/subscriptions.js diff --git a/services/gateway/apis/http-version3/methods/subscriptions.js b/services/gateway/apis/http-version3/methods/subscriptions.js new file mode 100644 index 000000000..abd00d9bd --- /dev/null +++ b/services/gateway/apis/http-version3/methods/subscriptions.js @@ -0,0 +1,28 @@ +/* + * LiskHQ/lisk-service + * Copyright © 2022 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + * + */ + +const subscriptionsSource = require('../../../sources/version3/subscriptions'); +const envelope = require('../../../sources/version3/mappings/stdEnvelope'); +const regex = require('../../../shared/regex'); +const { transformParams, response, getSwaggerDescription } = require('../../../shared/utils'); + +module.exports = { + version: '2.0', + swaggerApiPath: '/subscriptions', + tags: ['Subscriptions'], + source: subscriptionsSource, + envelope: {}, +}; From 0df27b929b40f03d38905442c01bef2cb6b9d107 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Tue, 16 May 2023 08:17:39 +0200 Subject: [PATCH 03/79] Add index logic for subscription:purchase --- .../subscription/purchase.js | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js new file mode 100644 index 000000000..9901733c1 --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js @@ -0,0 +1,94 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); + +const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const accountsTableSchema = require('../../../database/schema/accounts'); +const subscriptionsTableSchema = require('../../../database/schema/subscriptions'); + +const getAccountsTable = () => getTableInstance( + accountsTableSchema.tableName, + accountsTableSchema, + MYSQL_ENDPOINT, +); + +const getSubscriptionsTable = () => getTableInstance( + subscriptionsTableSchema.tableName, + subscriptionsTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'purchase'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const accountsTable = await getAccountsTable(); + const subscriptionsTable = await getSubscriptionsTable(); + + const oldAccount = accountsTable.find( + { address: getLisk32AddressFromPublicKey(tx.senderPublicKey) }, + dbTrx, + ); + + const oldIds = oldAccount ? oldAccount.subscription.owned : []; + + const account = { + address: getLisk32AddressFromPublicKey(tx.senderPublicKey), + subscription: { + owned: [...oldIds, dbTrx.id], + shared: dbTrx.id, + }, + }; + + logger.trace(`Updating account index for the account with address ${account.address}.`); + await accountsTable.upsert(account, dbTrx); + logger.debug(`Updated account index for the account with address ${account.address}.`); + + logger.trace(`Indexing subscription with address ${account.address}.`); + await subscriptionsTable.upsert(account, dbTrx); + logger.debug(`Indexed subscription with address ${account.address}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => { + const accountsTable = await getAccountsTable(); + const subscriptionsTable = await getSubscriptionsTable(); + + const oldAccount = accountsTable.find( + { address: getLisk32AddressFromPublicKey(tx.senderPublicKey) }, + dbTrx, + ); + + // Remove the validator details from the table on transaction reversal + const account = { + address: getLisk32AddressFromPublicKey(tx.senderPublicKey), + publicKey: tx.senderPublicKey, + subscription: { + owned: oldAccount.subscription.owned.filter(id => id !== dbTrx.id), + shared: oldAccount.subscription.shared === dbTrx.id ? null : oldAccount.subscription.shared, + }, + }; + + logger.trace(`Updating account index for the account with address ${account.address}.`); + await accountsTable.upsert(account, dbTrx); + logger.debug(`Updated account index for the account with address ${account.address}.`); + + logger.trace(`Remove subscription entry for address ${account.address}.`); + const subscriptionPK = account[subscriptionsTableSchema.primaryKey]; + await subscriptionsTable.deleteByPrimaryKey(subscriptionPK, dbTrx); + logger.debug(`Removed subscription entry for address ${account.address}.`); +}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From 897cabef272e7acc7f39eec6e8f1b62a7fe241a9 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Tue, 16 May 2023 08:18:04 +0200 Subject: [PATCH 04/79] Fix account indexing logoc of subscription:create --- .../transactionProcessor/subscription/create.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js index 0f9ebf08a..97c4da7d0 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js @@ -51,10 +51,19 @@ const revertTransaction = async (blockHeader, tx, events, dbTrx) => { const accountsTable = await getAccountsTable(); const subscriptionsTable = await getSubscriptionsTable(); + const oldAccount = accountsTable.find( + { address: getLisk32AddressFromPublicKey(tx.senderPublicKey) }, + dbTrx, + ); + // Remove the validator details from the table on transaction reversal const account = { address: getLisk32AddressFromPublicKey(tx.senderPublicKey), publicKey: tx.senderPublicKey, + subscription: { + owned: oldAccount.subscription.owned.filter(id => id !== dbTrx.id), + shared: null, + }, }; logger.trace(`Updating account index for the account with address ${account.address}.`); @@ -69,6 +78,6 @@ const revertTransaction = async (blockHeader, tx, events, dbTrx) => { module.exports = { COMMAND_NAME, - applyTransaction, - revertTransaction, + applyTransaction, + revertTransaction, }; From 1385fdb58c0acd9bb181abe1882b643cfe72c80b Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Tue, 16 May 2023 08:51:15 +0200 Subject: [PATCH 05/79] Add subscriptions to gateway --- .../http-version3/methods/subscriptions.js | 31 +++++- .../apis/http-version3/swagger/apiJson.json | 4 + .../swagger/definitions/subscriptions.json | 98 +++++++++++++++++++ .../swagger/parameters/subscriptions.json | 19 ++++ .../sources/version3/mappings/subscription.js | 25 +++++ .../gateway/sources/version3/subscriptions.js | 45 +++++++++ 6 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 services/gateway/apis/http-version3/swagger/definitions/subscriptions.json create mode 100644 services/gateway/apis/http-version3/swagger/parameters/subscriptions.json create mode 100644 services/gateway/sources/version3/mappings/subscription.js create mode 100644 services/gateway/sources/version3/subscriptions.js diff --git a/services/gateway/apis/http-version3/methods/subscriptions.js b/services/gateway/apis/http-version3/methods/subscriptions.js index abd00d9bd..352364173 100644 --- a/services/gateway/apis/http-version3/methods/subscriptions.js +++ b/services/gateway/apis/http-version3/methods/subscriptions.js @@ -22,7 +22,32 @@ const { transformParams, response, getSwaggerDescription } = require('../../../s module.exports = { version: '2.0', swaggerApiPath: '/subscriptions', - tags: ['Subscriptions'], - source: subscriptionsSource, - envelope: {}, + tags: ['Subscriptions'], + params: { + creatorAddress: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + subscriptionID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + }, + get schema() { + const subscriptionSchema = {}; + subscriptionSchema[this.swaggerApiPath] = { get: {} }; + subscriptionSchema[this.swaggerApiPath].get.tags = this.tags; + subscriptionSchema[this.swaggerApiPath].get.summary = 'Requests subscriptions data'; + subscriptionSchema[this.swaggerApiPath].get.description = getSwaggerDescription({ + rpcMethod: this.rpcMethod, + description: 'Returns subscriptions data', + }); + subscriptionSchema[this.swaggerApiPath].get.parameters = transformParams('subscriptions', this.params); + subscriptionSchema[this.swaggerApiPath].get.responses = { + 200: { + description: 'Returns a list of subscriptions', + schema: { + $ref: '#/definitions/subscriptionsWithEnvelope', + }, + }, + }; + Object.assign(subscriptionSchema[this.swaggerApiPath].get.responses, response); + return subscriptionSchema; + }, + source: subscriptionsSource, + envelope, }; diff --git a/services/gateway/apis/http-version3/swagger/apiJson.json b/services/gateway/apis/http-version3/swagger/apiJson.json index b85162776..1d76fada6 100644 --- a/services/gateway/apis/http-version3/swagger/apiJson.json +++ b/services/gateway/apis/http-version3/swagger/apiJson.json @@ -86,6 +86,10 @@ { "name": "Market", "description": "Market prices related API calls." + }, + { + "name": "Subscription", + "description": "Subscription module related API calls." } ], "schemes": [ diff --git a/services/gateway/apis/http-version3/swagger/definitions/subscriptions.json b/services/gateway/apis/http-version3/swagger/definitions/subscriptions.json new file mode 100644 index 000000000..f58e6b693 --- /dev/null +++ b/services/gateway/apis/http-version3/swagger/definitions/subscriptions.json @@ -0,0 +1,98 @@ +{ + "Subscription": { + "type": "object", + "required": [ + "senderAddress", + "members", + "price", + "consumable", + "maxMembers", + "streams" + ], + "properties": { + "subscriptionID": { + "type": "string", + "format": "id", + "example": "f9593f101c4acafc3ede650ab4c10fa2ecb59b225813eddbbb17b47e96932e9b", + "minLength": 1, + "maxLength": 64, + "description": "Unique identifier of the subscription.\nDerived from the subscription hash." + }, + "price": { + "type": "string", + "example": "0" + }, + "consumable": { + "type": "string", + "description": "Consumable value." + }, + "MaxMembers": { + "type": "integer", + "example": "1" + }, + "senderAddress": { + "type": "object", + "properties": { + "address": { + "type": "string", + "format": "address", + "example": "lskdwsyfmcko6mcd357446yatromr9vzgu7eb8y99", + "description": "The Lisk Address is the human-readable representation of a blockchain account.\nIt is 41 character long identifier that begins with `lsk`." + }, + "publicKey": { + "type": "string", + "format": "publicKey", + "example": "b1d6bc6c7edd0673f5fed0681b73de6eb70539c21278b300f07ade277e1962cd", + "description": "The public key is derived from the private key of the owner of the account.\nIt can be used to validate that the private key belongs to the owner, but not provide access to the owner's private key." + }, + "name": { + "type": "string", + "example": "genesis_84", + "description": "Delegate name" + } + } + }, + "members": { + "type": "array", + "items": { + "address": { + "type": "string", + "description": "Member address." + } + } + } + } + }, + "SubscriptionsWithEnvelope": { + "type": "object", + "required": [ + "data", + "meta" + ], + "properties": { + "data": { + "description": "List of subscriptions", + "type": "array", + "items": { + "$ref": "#/definitions/Subscriptions" + } + }, + "meta": { + "$ref": "#/definitions/pagination" + } + } + }, + "serverErrorEnvelope": { + "type": "object", + "properties": { + "error": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Unable to reach a network node" + } + } + } +} diff --git a/services/gateway/apis/http-version3/swagger/parameters/subscriptions.json b/services/gateway/apis/http-version3/swagger/parameters/subscriptions.json new file mode 100644 index 000000000..3eaf8b091 --- /dev/null +++ b/services/gateway/apis/http-version3/swagger/parameters/subscriptions.json @@ -0,0 +1,19 @@ +{ + "senderAddress": { + "name": "senderAddress", + "in": "query", + "description": "Lisk account address", + "type": "string", + "minLength": 3, + "maxLength": 41 + }, + "subscriptionID": { + "name": "subscriptionID", + "in": "query", + "description": "subscription ID to query", + "type": "string", + "format": "id", + "minLength": 1, + "maxLength": 64 + } +} diff --git a/services/gateway/sources/version3/mappings/subscription.js b/services/gateway/sources/version3/mappings/subscription.js new file mode 100644 index 000000000..bf79f6498 --- /dev/null +++ b/services/gateway/sources/version3/mappings/subscription.js @@ -0,0 +1,25 @@ +/* + * LiskHQ/lisk-service + * Copyright © 2022 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + * + */ +module.exports = { + subscriptionID: '=,string', + maxMembers: '=,number', + streams: '=,string', + price: '=,string', + consumable: '=,string', + members: ['data.members', { + address: '=,string', + }], +}; diff --git a/services/gateway/sources/version3/subscriptions.js b/services/gateway/sources/version3/subscriptions.js new file mode 100644 index 000000000..9a64176b4 --- /dev/null +++ b/services/gateway/sources/version3/subscriptions.js @@ -0,0 +1,45 @@ +/* + * LiskHQ/lisk-service + * Copyright © 2022 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + * + */ +const subscription = require('./mappings/subscription'); + +module.exports = { + type: 'moleculer', + method: 'indexer.subscriptions', + params: { + id: 'subscriptionID,string', + creatorAddress: '=,string', + price: '=,string', + consumed: '=,string', + members: ['data.members', { + address: '=,string', + }], + streams: '=,string', + maxMembers: '=,number', + limit: '=,number', + offset: '=,number', + sort: '=,string', + order: '=,string', + }, + definition: { + data: ['data', subscription], + meta: { + count: '=,number', + offset: '=,number', + total: '=,number', + }, + links: {}, + }, +}; From 1d799738316efc4af2ab9bc293b74fb50cadce33 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Thu, 18 May 2023 12:34:24 +0200 Subject: [PATCH 06/79] Added data mapping --- .../shared/sdk/constants/eventTopics.js | 9 +++ .../shared/sdk/constants/names.js | 9 +++ .../dataService/controllers/subscriptions.js | 31 +++++++++ .../dataService/modules/subscription.js | 29 +++++++++ .../shared/dataService/business/index.js | 4 ++ .../dataService/business/subscriptions.js | 43 +++++++++++++ .../shared/dataService/index.js | 4 ++ .../shared/dataService/subscriptions.js | 64 +++++++++++++++++++ .../shared/database/schema/subscriptions.js | 10 +-- .../subscription/updateMembers.js | 0 .../http-version3/methods/subscriptions.js | 1 + .../swagger/definitions/subscriptions.json | 1 + .../sources/version3/mappings/subscription.js | 1 + .../gateway/sources/version3/subscriptions.js | 2 +- 14 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 services/blockchain-indexer/methods/dataService/controllers/subscriptions.js create mode 100644 services/blockchain-indexer/methods/dataService/modules/subscription.js create mode 100644 services/blockchain-indexer/shared/dataService/business/subscriptions.js create mode 100644 services/blockchain-indexer/shared/dataService/subscriptions.js create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/updateMembers.js diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index 18a339e66..469ed8a42 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -73,6 +73,10 @@ const { MODULE_NAME_LEGACY, EVENT_NAME_ACCOUNT_RECLAIMED, EVENT_NAME_KEYS_REGISTERED, + + MODULE_NAME_SUBSCRIPTION, + EVENT_NAME_SUBSCRIPTION_CREATED, + EVENT_NAME_SUBSCRIPTION_PURCHASED, } = require('./names'); const COMMAND_EXECUTION_RESULT_TOPICS = ['transactionID']; @@ -140,6 +144,11 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { [EVENT_NAME_ACCOUNT_RECLAIMED]: ['transactionID', 'legacyAddress', 'newAddress'], [EVENT_NAME_KEYS_REGISTERED]: ['transactionID', 'validatorAddress', 'generatorKey', 'blsKey'], }, + // @todo what is this? + [MODULE_NAME_SUBSCRIPTION]: { + [EVENT_NAME_SUBSCRIPTION_CREATED]: ['defaultTopic', 'subscriptionID', 'creatorAddress'], + [EVENT_NAME_SUBSCRIPTION_PURCHASED]: ['defaultTopic', 'subscriptionID', 'creatorAddress'], + }, }; module.exports = { diff --git a/services/blockchain-connector/shared/sdk/constants/names.js b/services/blockchain-connector/shared/sdk/constants/names.js index 23de4b687..d51b60f04 100644 --- a/services/blockchain-connector/shared/sdk/constants/names.js +++ b/services/blockchain-connector/shared/sdk/constants/names.js @@ -85,6 +85,11 @@ const MODULE_NAME_LEGACY = 'legacy'; const EVENT_NAME_ACCOUNT_RECLAIMED = 'accountReclaimed'; const EVENT_NAME_KEYS_REGISTERED = 'keysRegistered'; +// Subscription +const MODULE_NAME_SUBSCRIPTION = 'subscription'; +const EVENT_NAME_SUBSCRIPTION_CREATED = 'subscriptionCreated'; +const EVENT_NAME_SUBSCRIPTION_PURCHASED = 'subscriptionPurchased'; + module.exports = { MODULE_NAME_AUTH, EVENT_NAME_MULTISIGNATURE_REGISTERED, @@ -146,4 +151,8 @@ module.exports = { MODULE_NAME_LEGACY, EVENT_NAME_ACCOUNT_RECLAIMED, EVENT_NAME_KEYS_REGISTERED, + + MODULE_NAME_SUBSCRIPTION, + EVENT_NAME_SUBSCRIPTION_CREATED, + EVENT_NAME_SUBSCRIPTION_PURCHASED, }; diff --git a/services/blockchain-indexer/methods/dataService/controllers/subscriptions.js b/services/blockchain-indexer/methods/dataService/controllers/subscriptions.js new file mode 100644 index 000000000..8c33085b5 --- /dev/null +++ b/services/blockchain-indexer/methods/dataService/controllers/subscriptions.js @@ -0,0 +1,31 @@ +const { + HTTP: { StatusCodes: { BAD_REQUEST } }, + Exceptions: { ValidationException, InvalidParamsException }, +} = require('lisk-service-framework'); + +const dataService = require('../../../shared/dataService'); + +const getSubscriptions = async params => { + const subscriptions = { + data: [], + meta: {}, + }; + + try { + const response = await dataService.getSubscriptions(params); + if (response.data) subscriptions.data = response.data; + if (response.meta) subscriptions.meta = response.meta; + + return subscriptions; + } catch (err) { + let status; + if (err instanceof InvalidParamsException) status = 'INVALID_PARAMS'; + if (err instanceof ValidationException) status = BAD_REQUEST; + if (status) return { status, data: { error: err.message } }; + throw err; + } +}; + +module.exports = { + getSubscriptions, +}; diff --git a/services/blockchain-indexer/methods/dataService/modules/subscription.js b/services/blockchain-indexer/methods/dataService/modules/subscription.js new file mode 100644 index 000000000..be2fbd4fb --- /dev/null +++ b/services/blockchain-indexer/methods/dataService/modules/subscription.js @@ -0,0 +1,29 @@ +/* + * LiskHQ/lisk-service + * Copyright © 2022 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + * + */ +const { + getSubscriptions, +} = require('../controllers/subscriptions'); + +module.exports = [ + { + name: 'subscriptions', + controller: getSubscriptions, + params: { + creatorAddress: { optional: true, type: 'string' }, + subscriptionID: { optional: true, type: 'string' }, + }, + }, +]; diff --git a/services/blockchain-indexer/shared/dataService/business/index.js b/services/blockchain-indexer/shared/dataService/business/index.js index e05a68a11..b3d5186ad 100644 --- a/services/blockchain-indexer/shared/dataService/business/index.js +++ b/services/blockchain-indexer/shared/dataService/business/index.js @@ -94,6 +94,7 @@ const { getNetworkDisconnectedPeers, getNetworkPeersStatistics, } = require('./network'); +const { getSubscriptions } = require('./subscriptions'); module.exports = { // Generators @@ -175,4 +176,7 @@ module.exports = { getNetworkConnectedPeers, getNetworkDisconnectedPeers, getNetworkPeersStatistics, + + // subscriptions + getSubscriptions, }; diff --git a/services/blockchain-indexer/shared/dataService/business/subscriptions.js b/services/blockchain-indexer/shared/dataService/business/subscriptions.js new file mode 100644 index 000000000..750b1728f --- /dev/null +++ b/services/blockchain-indexer/shared/dataService/business/subscriptions.js @@ -0,0 +1,43 @@ +const { + MySQL: { getTableInstance }, + Logger, +} = require('lisk-service-framework'); +const transactionsIndexSchema = require('../../database/schema/subscriptions'); +const config = require('../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; + +const getSubscriptionsIndex = () => getTableInstance( + transactionsIndexSchema.tableName, + transactionsIndexSchema, + MYSQL_ENDPOINT, +); + +const getSubscriptions = async (params = {}) => { + const subscriptionsTable = await getSubscriptionsIndex(); + logger.info('ALI: instantiating the table'); + + const total = await subscriptionsTable.count(params); + logger.info(`ALI: got the total: ${total}`); + const resultSet = await subscriptionsTable.find( + { ...params, limit: params.limit || total }, + ['price', 'consumable', 'maxMembers', 'streams'], + ); + logger.info(`ALI: got the result ${resultSet.length}`); + + const result = { + data: resultSet, + meta: { + count: resultSet.length, + offset: parseInt(params.offset, 10) || 0, + total, + }, + }; + return result; +}; + +module.exports = { + getSubscriptions, +}; diff --git a/services/blockchain-indexer/shared/dataService/index.js b/services/blockchain-indexer/shared/dataService/index.js index e36bf8a25..aa7870f4d 100644 --- a/services/blockchain-indexer/shared/dataService/index.js +++ b/services/blockchain-indexer/shared/dataService/index.js @@ -93,6 +93,7 @@ const { getIndexStatus } = require('./indexStatus'); const { getLegacyAccountInfo } = require('./legacy'); const { getValidator, validateBLSKey } = require('./validator'); const { getGenerators } = require('./generators'); +const { getSubscriptions } = require('./subscriptions'); module.exports = { // Blocks @@ -180,4 +181,7 @@ module.exports = { getAnnualInflation, getDefaultRewardAtHeight, getRewardConstants, + + // Subscriptions + getSubscriptions, }; diff --git a/services/blockchain-indexer/shared/dataService/subscriptions.js b/services/blockchain-indexer/shared/dataService/subscriptions.js new file mode 100644 index 000000000..7dc18a58a --- /dev/null +++ b/services/blockchain-indexer/shared/dataService/subscriptions.js @@ -0,0 +1,64 @@ +/* + * LiskHQ/lisk-service + * Copyright © 2022 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + * + */ +const { CacheRedis, Logger } = require('lisk-service-framework'); +const util = require('util'); + +const logger = Logger(); + +const business = require('./business'); + +// @todo implement this for subscriptions +const getTotalNumberOfSubscriptions = () => 10000; + +const getSubscriptions = async params => { + // Set default value + let data = []; + const meta = { + count: 0, + offset: parseInt(params.offset, 10) || 0, + total: 0, + }; + + // Store logs + if (params.subscriptionID) logger.debug(`Retrieved subscription with ID ${params.subscriptionID} from Lisk Core`); + else if (params.creatorAddress) logger.debug(`Retrieved subscription with creatorAddress: ${params.creatorAddress} from Lisk Core`); + else logger.debug(`Retrieved subscriptions with custom search: ${util.inspect(params)} from Lisk Core`); + + // Get data from server + const response = await business.getSubscriptions(params); + + // Normalize response + if (response.data) data = response.data; + if (response.meta) { + meta.count = response.meta.count || response.data.length; + if (params.subscriptionID) { + meta.total = response.data ? 1 : 0; + } else if (params.creatorAddress) { + meta.total = response.meta.total; + } else { + meta.total = await getTotalNumberOfSubscriptions(); + } + } + + return { + data, + meta, + }; +}; + +module.exports = { + getSubscriptions, +}; diff --git a/services/blockchain-indexer/shared/database/schema/subscriptions.js b/services/blockchain-indexer/shared/database/schema/subscriptions.js index 7fed48a96..e14462f3a 100644 --- a/services/blockchain-indexer/shared/database/schema/subscriptions.js +++ b/services/blockchain-indexer/shared/database/schema/subscriptions.js @@ -15,16 +15,18 @@ */ module.exports = { tableName: 'subscriptions', - primaryKey: 'address', + primaryKey: 'senderAddress', schema: { - address: { type: 'string' }, - publicKey: { type: 'string', null: true, defaultValue: null }, + // subscriptionID: { type: 'string' }, + senderAddress: { type: 'string', null: true, defaultValue: null }, price: { type: 'bigInteger', null: true, defaultValue: null }, consumable: { type: 'bigInteger', null: true, defaultValue: null }, streams: { type: 'bigInteger', null: true, defaultValue: null }, // members: @todo enable this as an empty array maxMembers: { type: 'integer', null: true, defaultValue: null }, }, - indexes: {}, + indexes: { + senderAddress: { type: 'string' }, + }, purge: {}, }; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/updateMembers.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/updateMembers.js new file mode 100644 index 000000000..e69de29bb diff --git a/services/gateway/apis/http-version3/methods/subscriptions.js b/services/gateway/apis/http-version3/methods/subscriptions.js index 352364173..f0a4c1fd5 100644 --- a/services/gateway/apis/http-version3/methods/subscriptions.js +++ b/services/gateway/apis/http-version3/methods/subscriptions.js @@ -22,6 +22,7 @@ const { transformParams, response, getSwaggerDescription } = require('../../../s module.exports = { version: '2.0', swaggerApiPath: '/subscriptions', + rpcMethod: 'get.subscriptions', tags: ['Subscriptions'], params: { creatorAddress: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, diff --git a/services/gateway/apis/http-version3/swagger/definitions/subscriptions.json b/services/gateway/apis/http-version3/swagger/definitions/subscriptions.json index f58e6b693..a4eeb4336 100644 --- a/services/gateway/apis/http-version3/swagger/definitions/subscriptions.json +++ b/services/gateway/apis/http-version3/swagger/definitions/subscriptions.json @@ -3,6 +3,7 @@ "type": "object", "required": [ "senderAddress", + "subscriptionID", "members", "price", "consumable", diff --git a/services/gateway/sources/version3/mappings/subscription.js b/services/gateway/sources/version3/mappings/subscription.js index bf79f6498..08366b1ce 100644 --- a/services/gateway/sources/version3/mappings/subscription.js +++ b/services/gateway/sources/version3/mappings/subscription.js @@ -15,6 +15,7 @@ */ module.exports = { subscriptionID: '=,string', + // creatorAddress: '=,string', maxMembers: '=,number', streams: '=,string', price: '=,string', diff --git a/services/gateway/sources/version3/subscriptions.js b/services/gateway/sources/version3/subscriptions.js index 9a64176b4..48d55b761 100644 --- a/services/gateway/sources/version3/subscriptions.js +++ b/services/gateway/sources/version3/subscriptions.js @@ -19,7 +19,7 @@ module.exports = { type: 'moleculer', method: 'indexer.subscriptions', params: { - id: 'subscriptionID,string', + subscriptionID: '=,string', creatorAddress: '=,string', price: '=,string', consumed: '=,string', From e5f24983ae83710b322102fb78d707383501ac38 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sun, 21 May 2023 14:38:58 +0200 Subject: [PATCH 07/79] Store subscription NFT --- .../shared/database/schema/subscriptions.js | 6 +++--- .../transactionProcessor/subscription/create.js | 14 ++++++++++++-- services/blockchain-indexer/shared/utils/nft.js | 12 ++++++++++++ .../swagger/definitions/subscriptions.json | 4 ++-- .../swagger/parameters/subscriptions.json | 4 ++-- .../sources/version3/mappings/subscription.js | 2 +- 6 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 services/blockchain-indexer/shared/utils/nft.js diff --git a/services/blockchain-indexer/shared/database/schema/subscriptions.js b/services/blockchain-indexer/shared/database/schema/subscriptions.js index e14462f3a..15c4e659c 100644 --- a/services/blockchain-indexer/shared/database/schema/subscriptions.js +++ b/services/blockchain-indexer/shared/database/schema/subscriptions.js @@ -15,10 +15,10 @@ */ module.exports = { tableName: 'subscriptions', - primaryKey: 'senderAddress', + primaryKey: 'subscriptionID', schema: { - // subscriptionID: { type: 'string' }, - senderAddress: { type: 'string', null: true, defaultValue: null }, + subscriptionID: { type: 'string', null: true, defaultValue: null }, + creatorAddress: { type: 'string', null: true, defaultValue: null }, price: { type: 'bigInteger', null: true, defaultValue: null }, consumable: { type: 'bigInteger', null: true, defaultValue: null }, streams: { type: 'bigInteger', null: true, defaultValue: null }, diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js index 97c4da7d0..d8f0305c2 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js @@ -4,6 +4,7 @@ const { } = require('lisk-service-framework'); const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); +const { getEntityID } = require('../../../utils/nft'); const config = require('../../../../config'); @@ -33,8 +34,10 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const accountsTable = await getAccountsTable(); const subscriptionsTable = await getSubscriptionsTable(); + const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); + const account = { - address: getLisk32AddressFromPublicKey(tx.senderPublicKey), + address: senderAddress, }; logger.trace(`Updating account index for the account with address ${account.address}.`); @@ -42,7 +45,14 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { logger.debug(`Updated account index for the account with address ${account.address}.`); logger.trace(`Indexing subscription with address ${account.address}.`); - await subscriptionsTable.upsert(account, dbTrx); + + const subscriptionsNFT = { + creatorAddress: senderAddress, + subscriptionID: getEntityID(tx), + ...tx.params, + }; + + await subscriptionsTable.upsert(subscriptionsNFT, dbTrx); logger.debug(`Indexed subscription with address ${account.address}.`); }; diff --git a/services/blockchain-indexer/shared/utils/nft.js b/services/blockchain-indexer/shared/utils/nft.js new file mode 100644 index 000000000..c33749ef6 --- /dev/null +++ b/services/blockchain-indexer/shared/utils/nft.js @@ -0,0 +1,12 @@ +const { createHash } = require('crypto'); +const { codec } = require('@liskhq/lisk-codec'); +const { baseTransactionSchema } = require('@liskhq/lisk-transactions'); + +const getEntityID = (transaction) => { + const txBytes = codec.encode(baseTransactionSchema, transaction); + return createHash('md5').update(txBytes).digest(); +}; + +module.exports = { + getEntityID, +}; diff --git a/services/gateway/apis/http-version3/swagger/definitions/subscriptions.json b/services/gateway/apis/http-version3/swagger/definitions/subscriptions.json index a4eeb4336..abc2410ec 100644 --- a/services/gateway/apis/http-version3/swagger/definitions/subscriptions.json +++ b/services/gateway/apis/http-version3/swagger/definitions/subscriptions.json @@ -2,7 +2,7 @@ "Subscription": { "type": "object", "required": [ - "senderAddress", + "creatorAddress", "subscriptionID", "members", "price", @@ -31,7 +31,7 @@ "type": "integer", "example": "1" }, - "senderAddress": { + "creatorAddress": { "type": "object", "properties": { "address": { diff --git a/services/gateway/apis/http-version3/swagger/parameters/subscriptions.json b/services/gateway/apis/http-version3/swagger/parameters/subscriptions.json index 3eaf8b091..90b8a19f1 100644 --- a/services/gateway/apis/http-version3/swagger/parameters/subscriptions.json +++ b/services/gateway/apis/http-version3/swagger/parameters/subscriptions.json @@ -1,6 +1,6 @@ { - "senderAddress": { - "name": "senderAddress", + "creatorAddress": { + "name": "creatorAddress", "in": "query", "description": "Lisk account address", "type": "string", diff --git a/services/gateway/sources/version3/mappings/subscription.js b/services/gateway/sources/version3/mappings/subscription.js index 08366b1ce..36095490f 100644 --- a/services/gateway/sources/version3/mappings/subscription.js +++ b/services/gateway/sources/version3/mappings/subscription.js @@ -15,7 +15,7 @@ */ module.exports = { subscriptionID: '=,string', - // creatorAddress: '=,string', + creatorAddress: '=,string', maxMembers: '=,number', streams: '=,string', price: '=,string', From 4d8cb4b3bc474115563600fc2c5b21a0ae241bde Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Wed, 24 May 2023 16:53:08 +0200 Subject: [PATCH 08/79] fix the subscription endpoint data --- .../shared/sdk/constants/eventTopics.js | 4 ++-- .../shared/dataService/business/subscriptions.js | 2 +- .../transactionProcessor/subscription/create.js | 5 +++-- services/blockchain-indexer/shared/utils/nft.js | 12 ------------ 4 files changed, 6 insertions(+), 17 deletions(-) delete mode 100644 services/blockchain-indexer/shared/utils/nft.js diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index 469ed8a42..1e4f35718 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -146,8 +146,8 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { }, // @todo what is this? [MODULE_NAME_SUBSCRIPTION]: { - [EVENT_NAME_SUBSCRIPTION_CREATED]: ['defaultTopic', 'subscriptionID', 'creatorAddress'], - [EVENT_NAME_SUBSCRIPTION_PURCHASED]: ['defaultTopic', 'subscriptionID', 'creatorAddress'], + [EVENT_NAME_SUBSCRIPTION_CREATED]: ['transactionID', 'senderAddress', 'subscriptionID'], + [EVENT_NAME_SUBSCRIPTION_PURCHASED]: ['transactionID', 'senderAddress', 'subscriptionID'], }, }; diff --git a/services/blockchain-indexer/shared/dataService/business/subscriptions.js b/services/blockchain-indexer/shared/dataService/business/subscriptions.js index 750b1728f..583c0806b 100644 --- a/services/blockchain-indexer/shared/dataService/business/subscriptions.js +++ b/services/blockchain-indexer/shared/dataService/business/subscriptions.js @@ -23,7 +23,7 @@ const getSubscriptions = async (params = {}) => { logger.info(`ALI: got the total: ${total}`); const resultSet = await subscriptionsTable.find( { ...params, limit: params.limit || total }, - ['price', 'consumable', 'maxMembers', 'streams'], + ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'maxMembers', 'streams'], ); logger.info(`ALI: got the result ${resultSet.length}`); diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js index d8f0305c2..280e106f7 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js @@ -4,7 +4,6 @@ const { } = require('lisk-service-framework'); const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); -const { getEntityID } = require('../../../utils/nft'); const config = require('../../../../config'); @@ -46,9 +45,11 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { logger.trace(`Indexing subscription with address ${account.address}.`); + const subscriptionID = events.find(e => e.module === 'subscription').topics[0]; + const subscriptionsNFT = { creatorAddress: senderAddress, - subscriptionID: getEntityID(tx), + subscriptionID, ...tx.params, }; diff --git a/services/blockchain-indexer/shared/utils/nft.js b/services/blockchain-indexer/shared/utils/nft.js deleted file mode 100644 index c33749ef6..000000000 --- a/services/blockchain-indexer/shared/utils/nft.js +++ /dev/null @@ -1,12 +0,0 @@ -const { createHash } = require('crypto'); -const { codec } = require('@liskhq/lisk-codec'); -const { baseTransactionSchema } = require('@liskhq/lisk-transactions'); - -const getEntityID = (transaction) => { - const txBytes = codec.encode(baseTransactionSchema, transaction); - return createHash('md5').update(txBytes).digest(); -}; - -module.exports = { - getEntityID, -}; From 3e7ed582eb4afff3677bf52bb1c11545ba6f09c0 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Wed, 24 May 2023 23:49:17 +0200 Subject: [PATCH 09/79] Fix event usage --- .../shared/sdk/constants/eventTopics.js | 4 ++-- .../indexer/transactionProcessor/subscription/create.js | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index 1e4f35718..e1a00cb04 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -146,8 +146,8 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { }, // @todo what is this? [MODULE_NAME_SUBSCRIPTION]: { - [EVENT_NAME_SUBSCRIPTION_CREATED]: ['transactionID', 'senderAddress', 'subscriptionID'], - [EVENT_NAME_SUBSCRIPTION_PURCHASED]: ['transactionID', 'senderAddress', 'subscriptionID'], + [EVENT_NAME_SUBSCRIPTION_CREATED]: ['transactionID', 'senderAddress'], + [EVENT_NAME_SUBSCRIPTION_PURCHASED]: ['transactionID', 'senderAddress'], }, }; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js index 280e106f7..af2ab248e 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js @@ -45,11 +45,11 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { logger.trace(`Indexing subscription with address ${account.address}.`); - const subscriptionID = events.find(e => e.module === 'subscription').topics[0]; + // @todo make sure the process won't break if the event doesn't exist. e.g. do not index. + const { data: eventData } = events.find(e => e.module === 'subscription' && e.name === 'subscriptionCreated'); const subscriptionsNFT = { - creatorAddress: senderAddress, - subscriptionID, + ...eventData, ...tx.params, }; From 52ca6c5bbdd3bb37157ccf63713ccb7f049d399d Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sun, 28 May 2023 11:38:02 +0200 Subject: [PATCH 10/79] Clean logs and update unit tests --- .../dataService/modules/subscription.js | 17 +------ .../dataService/business/subscriptions.js | 6 --- .../shared/dataService/subscriptions.js | 46 +------------------ .../shared/database/schema/subscriptions.js | 16 ------- .../subscription/create.js | 4 +- .../subscription/purchase.js | 4 +- .../gateway/tests/constants/generateDocs.js | 31 +++++++++++++ .../gateway/tests/constants/registerApi.js | 4 ++ 8 files changed, 43 insertions(+), 85 deletions(-) diff --git a/services/blockchain-indexer/methods/dataService/modules/subscription.js b/services/blockchain-indexer/methods/dataService/modules/subscription.js index be2fbd4fb..0d39391ce 100644 --- a/services/blockchain-indexer/methods/dataService/modules/subscription.js +++ b/services/blockchain-indexer/methods/dataService/modules/subscription.js @@ -1,18 +1,3 @@ -/* - * LiskHQ/lisk-service - * Copyright © 2022 Lisk Foundation - * - * See the LICENSE file at the top-level directory of this distribution - * for licensing information. - * - * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, - * no part of this software, including this file, may be copied, modified, - * propagated, or distributed except according to the terms contained in the - * LICENSE file. - * - * Removal or modification of this copyright notice is prohibited. - * - */ const { getSubscriptions, } = require('../controllers/subscriptions'); @@ -24,6 +9,8 @@ module.exports = [ params: { creatorAddress: { optional: true, type: 'string' }, subscriptionID: { optional: true, type: 'string' }, + limit: { optional: true, type: 'number' }, + offset: { optional: true, type: 'number' }, }, }, ]; diff --git a/services/blockchain-indexer/shared/dataService/business/subscriptions.js b/services/blockchain-indexer/shared/dataService/business/subscriptions.js index 583c0806b..b21ca4291 100644 --- a/services/blockchain-indexer/shared/dataService/business/subscriptions.js +++ b/services/blockchain-indexer/shared/dataService/business/subscriptions.js @@ -1,12 +1,9 @@ const { MySQL: { getTableInstance }, - Logger, } = require('lisk-service-framework'); const transactionsIndexSchema = require('../../database/schema/subscriptions'); const config = require('../../../config'); -const logger = Logger(); - const MYSQL_ENDPOINT = config.endpoints.mysql; const getSubscriptionsIndex = () => getTableInstance( @@ -17,15 +14,12 @@ const getSubscriptionsIndex = () => getTableInstance( const getSubscriptions = async (params = {}) => { const subscriptionsTable = await getSubscriptionsIndex(); - logger.info('ALI: instantiating the table'); const total = await subscriptionsTable.count(params); - logger.info(`ALI: got the total: ${total}`); const resultSet = await subscriptionsTable.find( { ...params, limit: params.limit || total }, ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'maxMembers', 'streams'], ); - logger.info(`ALI: got the result ${resultSet.length}`); const result = { data: resultSet, diff --git a/services/blockchain-indexer/shared/dataService/subscriptions.js b/services/blockchain-indexer/shared/dataService/subscriptions.js index 7dc18a58a..af8c10ec9 100644 --- a/services/blockchain-indexer/shared/dataService/subscriptions.js +++ b/services/blockchain-indexer/shared/dataService/subscriptions.js @@ -1,37 +1,11 @@ -/* - * LiskHQ/lisk-service - * Copyright © 2022 Lisk Foundation - * - * See the LICENSE file at the top-level directory of this distribution - * for licensing information. - * - * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, - * no part of this software, including this file, may be copied, modified, - * propagated, or distributed except according to the terms contained in the - * LICENSE file. - * - * Removal or modification of this copyright notice is prohibited. - * - */ -const { CacheRedis, Logger } = require('lisk-service-framework'); +const { Logger } = require('lisk-service-framework'); const util = require('util'); const logger = Logger(); const business = require('./business'); -// @todo implement this for subscriptions -const getTotalNumberOfSubscriptions = () => 10000; - const getSubscriptions = async params => { - // Set default value - let data = []; - const meta = { - count: 0, - offset: parseInt(params.offset, 10) || 0, - total: 0, - }; - // Store logs if (params.subscriptionID) logger.debug(`Retrieved subscription with ID ${params.subscriptionID} from Lisk Core`); else if (params.creatorAddress) logger.debug(`Retrieved subscription with creatorAddress: ${params.creatorAddress} from Lisk Core`); @@ -40,23 +14,7 @@ const getSubscriptions = async params => { // Get data from server const response = await business.getSubscriptions(params); - // Normalize response - if (response.data) data = response.data; - if (response.meta) { - meta.count = response.meta.count || response.data.length; - if (params.subscriptionID) { - meta.total = response.data ? 1 : 0; - } else if (params.creatorAddress) { - meta.total = response.meta.total; - } else { - meta.total = await getTotalNumberOfSubscriptions(); - } - } - - return { - data, - meta, - }; + return response; }; module.exports = { diff --git a/services/blockchain-indexer/shared/database/schema/subscriptions.js b/services/blockchain-indexer/shared/database/schema/subscriptions.js index 15c4e659c..20b179602 100644 --- a/services/blockchain-indexer/shared/database/schema/subscriptions.js +++ b/services/blockchain-indexer/shared/database/schema/subscriptions.js @@ -1,18 +1,3 @@ -/* - * LiskHQ/lisk-service - * Copyright © 2022 Lisk Foundation - * - * See the LICENSE file at the top-level directory of this distribution - * for licensing information. - * - * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, - * no part of this software, including this file, may be copied, modified, - * propagated, or distributed except according to the terms contained in the - * LICENSE file. - * - * Removal or modification of this copyright notice is prohibited. - * - */ module.exports = { tableName: 'subscriptions', primaryKey: 'subscriptionID', @@ -22,7 +7,6 @@ module.exports = { price: { type: 'bigInteger', null: true, defaultValue: null }, consumable: { type: 'bigInteger', null: true, defaultValue: null }, streams: { type: 'bigInteger', null: true, defaultValue: null }, - // members: @todo enable this as an empty array maxMembers: { type: 'integer', null: true, defaultValue: null }, }, indexes: { diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js index af2ab248e..001912eb3 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js @@ -54,7 +54,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { }; await subscriptionsTable.upsert(subscriptionsNFT, dbTrx); - logger.debug(`Indexed subscription with address ${account.address}.`); + logger.debug(`Indexed subscription with ID ${eventData.subscriptionID}.`); }; // eslint-disable-next-line no-unused-vars @@ -84,7 +84,7 @@ const revertTransaction = async (blockHeader, tx, events, dbTrx) => { logger.trace(`Remove subscription entry for address ${account.address}.`); const subscriptionPK = account[subscriptionsTableSchema.primaryKey]; await subscriptionsTable.deleteByPrimaryKey(subscriptionPK, dbTrx); - logger.debug(`Removed subscription entry for address ${account.address}.`); + logger.debug(`Removed subscription entry for ID ${subscriptionPK}.`); }; module.exports = { diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js index 9901733c1..3ea39ad29 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js @@ -54,7 +54,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { logger.trace(`Indexing subscription with address ${account.address}.`); await subscriptionsTable.upsert(account, dbTrx); - logger.debug(`Indexed subscription with address ${account.address}.`); + logger.debug(`Indexed subscription with ID ${dbTrx.id}.`); }; // eslint-disable-next-line no-unused-vars @@ -84,7 +84,7 @@ const revertTransaction = async (blockHeader, tx, events, dbTrx) => { logger.trace(`Remove subscription entry for address ${account.address}.`); const subscriptionPK = account[subscriptionsTableSchema.primaryKey]; await subscriptionsTable.deleteByPrimaryKey(subscriptionPK, dbTrx); - logger.debug(`Removed subscription entry for address ${account.address}.`); + logger.debug(`Removed subscription entry for ID ${subscriptionPK}.`); }; module.exports = { diff --git a/services/gateway/tests/constants/generateDocs.js b/services/gateway/tests/constants/generateDocs.js index 39344b3c9..03fd436ee 100644 --- a/services/gateway/tests/constants/generateDocs.js +++ b/services/gateway/tests/constants/generateDocs.js @@ -692,6 +692,37 @@ const createApiDocsExpectedResponse = { }, }, }, + '/subscriptions': { + get: { + description: 'Returns subscriptions data\n RPC => get.subscriptions', + parameters: [ + { + $ref: '#/parameters/creatorAddress', + }, + { + $ref: '#/parameters/subscriptionID', + }, + ], + responses: { + 200: { + description: 'Returns a list of subscriptions', + schema: { + $ref: '#/definitions/subscriptionsWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, + }, + }, + summary: 'Requests subscriptions data', + tags: [ + 'Subscriptions', + ], + }, + }, '/transactions': { get: { tags: [ diff --git a/services/gateway/tests/constants/registerApi.js b/services/gateway/tests/constants/registerApi.js index 0eb8ea0d5..749ea219c 100644 --- a/services/gateway/tests/constants/registerApi.js +++ b/services/gateway/tests/constants/registerApi.js @@ -36,6 +36,7 @@ const expectedResponseForRegisterHttpApi = { 'indexer.transactions.post', 'indexer.schemas', 'gateway.spec', + 'indexer.subscriptions', 'indexer.transactions', 'indexer.transactions.dryrun', 'statistics.transactions.statistics', @@ -76,6 +77,7 @@ const expectedResponseForRegisterHttpApi = { 'GET network/status': 'indexer.network.status', 'POST transactions': 'indexer.transactions.post', 'GET schemas': 'indexer.schemas', + 'GET subscriptions': 'indexer.subscriptions', 'GET spec': 'gateway.spec', 'GET transactions': 'indexer.transactions', 'POST transactions/dryrun': 'indexer.transactions.dryrun', @@ -123,6 +125,7 @@ const expectedResponseForRegisterRpcApi = { 'indexer.network.status', 'indexer.transactions.post', 'indexer.schemas', + 'indexer.subscriptions', 'indexer.transactions', 'indexer.transactions.dryrun', 'statistics.transactions.statistics', @@ -162,6 +165,7 @@ const expectedResponseForRegisterRpcApi = { 'get.network.status': 'indexer.network.status', 'post.transactions': 'indexer.transactions.post', 'get.schemas': 'indexer.schemas', + 'get.subscriptions': 'indexer.subscriptions', 'get.transactions': 'indexer.transactions', 'post.transactions.dryrun': 'indexer.transactions.dryrun', 'get.transactions.statistics': 'statistics.transactions.statistics', From 16b25249934c602104234de6b2b6209608f2fedd Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sun, 28 May 2023 11:38:54 +0200 Subject: [PATCH 11/79] Temporarily disable code coverage upload to codecov --- .github/workflows/branch-coverage.yml | 8 -------- .github/workflows/pr-coverage.yml | 8 -------- 2 files changed, 16 deletions(-) diff --git a/.github/workflows/branch-coverage.yml b/.github/workflows/branch-coverage.yml index e694af53d..9e6929122 100644 --- a/.github/workflows/branch-coverage.yml +++ b/.github/workflows/branch-coverage.yml @@ -18,11 +18,3 @@ jobs: run: make build-local - name: Check test coverage run: npm run test:coverage - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: true - files: coverage-final.json - name: codecov-umbrella - verbose: true diff --git a/.github/workflows/pr-coverage.yml b/.github/workflows/pr-coverage.yml index 6d91cf5f9..9011b7f87 100644 --- a/.github/workflows/pr-coverage.yml +++ b/.github/workflows/pr-coverage.yml @@ -14,11 +14,3 @@ jobs: run: make build-local - name: Check test coverage run: npm run test:coverage - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - fail_ci_if_error: true - files: coverage-final.json - name: codecov-umbrella - verbose: true From 48f4767b3d908371bd33826507d7e8075fdbd70e Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 29 May 2023 10:26:26 +0200 Subject: [PATCH 12/79] Add subscription purchase logic --- .../shared/sdk/constants/eventTopics.js | 1 - .../dataService/business/subscriptions.js | 29 +++++- .../shared/database/schema/members.js | 12 +++ .../subscription/create.js | 42 ++------ .../subscription/purchase.js | 99 ++++++++++++------- .../sources/version3/mappings/subscription.js | 4 +- .../gateway/sources/version3/subscriptions.js | 4 +- 7 files changed, 114 insertions(+), 77 deletions(-) create mode 100644 services/blockchain-indexer/shared/database/schema/members.js diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index e1a00cb04..8eb657ec0 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -144,7 +144,6 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { [EVENT_NAME_ACCOUNT_RECLAIMED]: ['transactionID', 'legacyAddress', 'newAddress'], [EVENT_NAME_KEYS_REGISTERED]: ['transactionID', 'validatorAddress', 'generatorKey', 'blsKey'], }, - // @todo what is this? [MODULE_NAME_SUBSCRIPTION]: { [EVENT_NAME_SUBSCRIPTION_CREATED]: ['transactionID', 'senderAddress'], [EVENT_NAME_SUBSCRIPTION_PURCHASED]: ['transactionID', 'senderAddress'], diff --git a/services/blockchain-indexer/shared/dataService/business/subscriptions.js b/services/blockchain-indexer/shared/dataService/business/subscriptions.js index b21ca4291..7d385d878 100644 --- a/services/blockchain-indexer/shared/dataService/business/subscriptions.js +++ b/services/blockchain-indexer/shared/dataService/business/subscriptions.js @@ -1,7 +1,10 @@ const { MySQL: { getTableInstance }, } = require('lisk-service-framework'); +const BluebirdPromise = require('bluebird'); + const transactionsIndexSchema = require('../../database/schema/subscriptions'); +const membersIndexSchema = require('../../database/schema/members'); const config = require('../../../config'); const MYSQL_ENDPOINT = config.endpoints.mysql; @@ -12,19 +15,39 @@ const getSubscriptionsIndex = () => getTableInstance( MYSQL_ENDPOINT, ); +const getMembersIndex = () => getTableInstance( + membersIndexSchema.tableName, + membersIndexSchema, + MYSQL_ENDPOINT, +); + const getSubscriptions = async (params = {}) => { const subscriptionsTable = await getSubscriptionsIndex(); + const membersTable = await getMembersIndex(); const total = await subscriptionsTable.count(params); - const resultSet = await subscriptionsTable.find( + const subscriptionSet = await subscriptionsTable.find( { ...params, limit: params.limit || total }, ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'maxMembers', 'streams'], ); + const data = await BluebirdPromise.map( + subscriptionSet, + async subscription => { + const membersSet = await membersTable.find( + { shared: subscription.subscriptionID }, + ['address'], + ); + subscription.members = membersSet.map(member => ({ address: member.address })); + return subscription; + }, + { concurrency: subscriptionSet.length }, + ); + const result = { - data: resultSet, + data, meta: { - count: resultSet.length, + count: data.length, offset: parseInt(params.offset, 10) || 0, total, }, diff --git a/services/blockchain-indexer/shared/database/schema/members.js b/services/blockchain-indexer/shared/database/schema/members.js new file mode 100644 index 000000000..e57067d5f --- /dev/null +++ b/services/blockchain-indexer/shared/database/schema/members.js @@ -0,0 +1,12 @@ +module.exports = { + tableName: 'members', + primaryKey: 'address', + schema: { + address: { type: 'string' }, + shared: { type: 'string', null: true }, + }, + indexes: { + shared: { type: 'key' }, + }, + purge: {}, +}; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js index 001912eb3..0247c14c3 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js @@ -34,18 +34,13 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const subscriptionsTable = await getSubscriptionsTable(); const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); + const devAccount = { address: senderAddress }; - const account = { - address: senderAddress, - }; - - logger.trace(`Updating account index for the account with address ${account.address}.`); - await accountsTable.upsert(account, dbTrx); - logger.debug(`Updated account index for the account with address ${account.address}.`); + logger.trace(`Updating account index for the account with address ${senderAddress}.`); + await accountsTable.upsert(devAccount, dbTrx); + logger.debug(`Updated account index for the account with address ${senderAddress}.`); - logger.trace(`Indexing subscription with address ${account.address}.`); - - // @todo make sure the process won't break if the event doesn't exist. e.g. do not index. + logger.trace(`Indexing subscription with address ${senderAddress}.`); const { data: eventData } = events.find(e => e.module === 'subscription' && e.name === 'subscriptionCreated'); const subscriptionsNFT = { @@ -59,32 +54,13 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { // eslint-disable-next-line no-unused-vars const revertTransaction = async (blockHeader, tx, events, dbTrx) => { - const accountsTable = await getAccountsTable(); const subscriptionsTable = await getSubscriptionsTable(); - const oldAccount = accountsTable.find( - { address: getLisk32AddressFromPublicKey(tx.senderPublicKey) }, - dbTrx, - ); - - // Remove the validator details from the table on transaction reversal - const account = { - address: getLisk32AddressFromPublicKey(tx.senderPublicKey), - publicKey: tx.senderPublicKey, - subscription: { - owned: oldAccount.subscription.owned.filter(id => id !== dbTrx.id), - shared: null, - }, - }; - - logger.trace(`Updating account index for the account with address ${account.address}.`); - await accountsTable.upsert(account, dbTrx); - logger.debug(`Updated account index for the account with address ${account.address}.`); + const { data: eventData } = events.find(e => e.module === 'subscription' && e.name === 'subscriptionCreated'); - logger.trace(`Remove subscription entry for address ${account.address}.`); - const subscriptionPK = account[subscriptionsTableSchema.primaryKey]; - await subscriptionsTable.deleteByPrimaryKey(subscriptionPK, dbTrx); - logger.debug(`Removed subscription entry for ID ${subscriptionPK}.`); + logger.trace(`Remove subscription entry for ID ${eventData.subscriptionID}.`); + await subscriptionsTable.delete({ subscriptionID: eventData.subscriptionID }, dbTrx); + logger.debug(`Removed subscription entry for ID ${eventData.subscriptionID}.`); }; module.exports = { diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js index 3ea39ad29..eb937eb45 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js @@ -2,6 +2,7 @@ const { Logger, MySQL: { getTableInstance }, } = require('lisk-service-framework'); +const BluebirdPromise = require('bluebird'); const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); @@ -12,6 +13,7 @@ const logger = Logger(); const MYSQL_ENDPOINT = config.endpoints.mysql; const accountsTableSchema = require('../../../database/schema/accounts'); const subscriptionsTableSchema = require('../../../database/schema/subscriptions'); +const membersTableSchema = require('../../../database/schema/members'); const getAccountsTable = () => getTableInstance( accountsTableSchema.tableName, @@ -25,6 +27,12 @@ const getSubscriptionsTable = () => getTableInstance( MYSQL_ENDPOINT, ); +const getMembersTable = () => getTableInstance( + membersTableSchema.tableName, + membersTableSchema, + MYSQL_ENDPOINT, +); + // Command specific constants const COMMAND_NAME = 'purchase'; @@ -32,59 +40,74 @@ const COMMAND_NAME = 'purchase'; const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const accountsTable = await getAccountsTable(); const subscriptionsTable = await getSubscriptionsTable(); + const membersTable = await getMembersTable(); + + const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); + const { subscriptionID } = tx.params; - const oldAccount = accountsTable.find( - { address: getLisk32AddressFromPublicKey(tx.senderPublicKey) }, + logger.trace(`Indexing subscription with address ${subscriptionID}.`); + const subscriptionNFT = await subscriptionsTable.find( + { subscriptionID }, + ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'streams', 'maxMembers'], dbTrx, ); + subscriptionNFT.creatorAddress = senderAddress; + await subscriptionsTable.upsert(subscriptionNFT, dbTrx); + logger.debug(`Indexed subscription with ID ${dbTrx.id}.`); - const oldIds = oldAccount ? oldAccount.subscription.owned : []; - - const account = { - address: getLisk32AddressFromPublicKey(tx.senderPublicKey), - subscription: { - owned: [...oldIds, dbTrx.id], - shared: dbTrx.id, + // Update members in accounts and members table + await BluebirdPromise.map( + tx.params.members, + async member => { + const oldAccount = { address: member }; + logger.trace(`Updating account index for the account with address ${member}.`); + await accountsTable.upsert(oldAccount, dbTrx); + logger.debug(`Updated account index for the account with address ${member}.`); + + const memberData = { + address: member, + shared: subscriptionID, + }; + + logger.trace(`Updating member index for the member with address ${member}.`); + await membersTable.upsert(memberData, dbTrx); + logger.debug(`Updated member index for the member with address ${member}.`); + return true; }, - }; - - logger.trace(`Updating account index for the account with address ${account.address}.`); - await accountsTable.upsert(account, dbTrx); - logger.debug(`Updated account index for the account with address ${account.address}.`); + { concurrency: tx.params.members.length }, + ); - logger.trace(`Indexing subscription with address ${account.address}.`); - await subscriptionsTable.upsert(account, dbTrx); - logger.debug(`Indexed subscription with ID ${dbTrx.id}.`); + // Update sender in accounts table + const senderAccount = { address: senderAddress }; + await accountsTable.upsert(senderAccount, dbTrx); + logger.trace(`Indexed subscription purchase with account address ${senderAddress}.`); }; // eslint-disable-next-line no-unused-vars const revertTransaction = async (blockHeader, tx, events, dbTrx) => { - const accountsTable = await getAccountsTable(); const subscriptionsTable = await getSubscriptionsTable(); + const membersTable = await getMembersTable(); - const oldAccount = accountsTable.find( - { address: getLisk32AddressFromPublicKey(tx.senderPublicKey) }, + const lisk32DevAddress = 'lskh96jgzfftzff2fta2zvsmba9mvs5cnz9ahr3ke'; + const { subscriptionID } = tx.params; + + const subscriptionNFT = await subscriptionsTable.find( + { subscriptionID }, + ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'streams', 'maxMembers'], dbTrx, ); - - // Remove the validator details from the table on transaction reversal - const account = { - address: getLisk32AddressFromPublicKey(tx.senderPublicKey), - publicKey: tx.senderPublicKey, - subscription: { - owned: oldAccount.subscription.owned.filter(id => id !== dbTrx.id), - shared: oldAccount.subscription.shared === dbTrx.id ? null : oldAccount.subscription.shared, + subscriptionNFT.creatorAddress = lisk32DevAddress; + await subscriptionsTable.upsert(subscriptionNFT, dbTrx); + + await BluebirdPromise.map( + tx.params.member, + async member => { + logger.trace(`Remove member index for the members with address ${member}.`); + await membersTable.deleteByPrimaryKey(member, dbTrx); + logger.debug(`Updated member index for the members with address ${member}.`); }, - }; - - logger.trace(`Updating account index for the account with address ${account.address}.`); - await accountsTable.upsert(account, dbTrx); - logger.debug(`Updated account index for the account with address ${account.address}.`); - - logger.trace(`Remove subscription entry for address ${account.address}.`); - const subscriptionPK = account[subscriptionsTableSchema.primaryKey]; - await subscriptionsTable.deleteByPrimaryKey(subscriptionPK, dbTrx); - logger.debug(`Removed subscription entry for ID ${subscriptionPK}.`); + { concurrency: tx.params.member.length }, + ); }; module.exports = { diff --git a/services/gateway/sources/version3/mappings/subscription.js b/services/gateway/sources/version3/mappings/subscription.js index 36095490f..bf82ee71b 100644 --- a/services/gateway/sources/version3/mappings/subscription.js +++ b/services/gateway/sources/version3/mappings/subscription.js @@ -20,7 +20,9 @@ module.exports = { streams: '=,string', price: '=,string', consumable: '=,string', - members: ['data.members', { + members: ['members', { address: '=,string', + name: '=,string', + publicKey: '=,string', }], }; diff --git a/services/gateway/sources/version3/subscriptions.js b/services/gateway/sources/version3/subscriptions.js index 48d55b761..45277ffaf 100644 --- a/services/gateway/sources/version3/subscriptions.js +++ b/services/gateway/sources/version3/subscriptions.js @@ -23,8 +23,10 @@ module.exports = { creatorAddress: '=,string', price: '=,string', consumed: '=,string', - members: ['data.members', { + members: ['members', { address: '=,string', + name: '=,string', + publicKey: '=,string', }], streams: '=,string', maxMembers: '=,number', From a2792d73c2216dc599767cd1b23cb7f2df7e9dfd Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 29 May 2023 10:33:57 +0200 Subject: [PATCH 13/79] Move dev account to constants --- services/blockchain-indexer/shared/constants.js | 4 ++++ .../indexer/transactionProcessor/subscription/purchase.js | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/services/blockchain-indexer/shared/constants.js b/services/blockchain-indexer/shared/constants.js index 2dc26930c..a7d755638 100644 --- a/services/blockchain-indexer/shared/constants.js +++ b/services/blockchain-indexer/shared/constants.js @@ -132,6 +132,9 @@ const TRANSACTION_VERIFY_RESULT = { OK: 1, }; +// @todo retrieve this from Core +const DEV_ADDRESS = 'lskh96jgzfftzff2fta2zvsmba9mvs5cnz9ahr3ke'; + module.exports = { updateFinalizedHeight, getFinalizedHeight, @@ -155,4 +158,5 @@ module.exports = { KV_STORE_KEY, TRANSACTION_STATUS, TRANSACTION_VERIFY_RESULT, + DEV_ADDRESS, }; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js index eb937eb45..cbeb792b1 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js @@ -5,6 +5,7 @@ const { const BluebirdPromise = require('bluebird'); const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); +const { DEV_ADDRESS } = require('../../../constants'); const config = require('../../../../config'); @@ -88,7 +89,6 @@ const revertTransaction = async (blockHeader, tx, events, dbTrx) => { const subscriptionsTable = await getSubscriptionsTable(); const membersTable = await getMembersTable(); - const lisk32DevAddress = 'lskh96jgzfftzff2fta2zvsmba9mvs5cnz9ahr3ke'; const { subscriptionID } = tx.params; const subscriptionNFT = await subscriptionsTable.find( @@ -96,7 +96,7 @@ const revertTransaction = async (blockHeader, tx, events, dbTrx) => { ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'streams', 'maxMembers'], dbTrx, ); - subscriptionNFT.creatorAddress = lisk32DevAddress; + subscriptionNFT.creatorAddress = DEV_ADDRESS; await subscriptionsTable.upsert(subscriptionNFT, dbTrx); await BluebirdPromise.map( From 3e678ea70ebd8db8ea8cd1fda0455f2868f1fcc0 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Wed, 31 May 2023 07:52:16 +0200 Subject: [PATCH 14/79] Fix the members indexing logic --- .../dataService/business/subscriptions.js | 2 +- .../shared/database/schema/members.js | 5 +- .../subscription/purchase.js | 12 +- .../subscription/updateMembers.js | 105 ++++++++++++++++++ 4 files changed, 113 insertions(+), 11 deletions(-) diff --git a/services/blockchain-indexer/shared/dataService/business/subscriptions.js b/services/blockchain-indexer/shared/dataService/business/subscriptions.js index 7d385d878..07f67d287 100644 --- a/services/blockchain-indexer/shared/dataService/business/subscriptions.js +++ b/services/blockchain-indexer/shared/dataService/business/subscriptions.js @@ -35,7 +35,7 @@ const getSubscriptions = async (params = {}) => { subscriptionSet, async subscription => { const membersSet = await membersTable.find( - { shared: subscription.subscriptionID }, + { shared: subscription.subscriptionID, removedBy: null }, ['address'], ); subscription.members = membersSet.map(member => ({ address: member.address })); diff --git a/services/blockchain-indexer/shared/database/schema/members.js b/services/blockchain-indexer/shared/database/schema/members.js index e57067d5f..de740138b 100644 --- a/services/blockchain-indexer/shared/database/schema/members.js +++ b/services/blockchain-indexer/shared/database/schema/members.js @@ -1,9 +1,12 @@ module.exports = { tableName: 'members', - primaryKey: 'address', + primaryKey: 'id', schema: { + id: { type: 'string' }, address: { type: 'string' }, shared: { type: 'string', null: true }, + addedBy: { type: 'string', null: true }, + removedBy: { type: 'string', null: true }, }, indexes: { shared: { type: 'key' }, diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js index cbeb792b1..1ebd9035f 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js @@ -66,7 +66,9 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { logger.debug(`Updated account index for the account with address ${member}.`); const memberData = { + id: member.concat(`-${tx.nonce.toString()}`), address: member, + addedBy: tx.id, shared: subscriptionID, }; @@ -99,15 +101,7 @@ const revertTransaction = async (blockHeader, tx, events, dbTrx) => { subscriptionNFT.creatorAddress = DEV_ADDRESS; await subscriptionsTable.upsert(subscriptionNFT, dbTrx); - await BluebirdPromise.map( - tx.params.member, - async member => { - logger.trace(`Remove member index for the members with address ${member}.`); - await membersTable.deleteByPrimaryKey(member, dbTrx); - logger.debug(`Updated member index for the members with address ${member}.`); - }, - { concurrency: tx.params.member.length }, - ); + await membersTable.delete({ shared: subscriptionID }, dbTrx); }; module.exports = { diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/updateMembers.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/updateMembers.js index e69de29bb..e2c035c2d 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/updateMembers.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/updateMembers.js @@ -0,0 +1,105 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); +const BluebirdPromise = require('bluebird'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const membersTableSchema = require('../../../database/schema/members'); + +const getMembersTable = () => getTableInstance( + membersTableSchema.tableName, + membersTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'updateMembers'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const membersTable = await getMembersTable(); + + const { subscriptionID } = tx.params; + + logger.trace(`Removing existing members with subscription ID ${subscriptionID}.`); + const currentMembers = await membersTable.find( + { shared: subscriptionID, removedBy: null }, + ['id', 'address', 'shared'], + dbTrx, + ); + + const currentAddresses = currentMembers.map(({ address }) => address); + const removed = currentMembers.filter(({ address }) => !tx.params.members.includes(address)); + const added = tx.params.members.filter(member => !currentAddresses.includes(member)); + + await BluebirdPromise.map( + removed, + async member => { + const memberData = { + ...member, + removedBy: tx.id, + }; + logger.trace(`Updating account index for the account with address ${member}.`); + await membersTable.upsert(memberData, dbTrx); + logger.debug(`Updated account index for the account with address ${member}.`); + }, + { concurrency: removed.length }, + ); + logger.trace(`Removed existing members with subscription ID ${subscriptionID}.`); + + logger.trace(`Adding new members with subscription ID ${subscriptionID}.`); + await BluebirdPromise.map( + added, + async member => { + const memberData = { + id: member.concat(`-${tx.nonce.toString()}`), + address: member, + addedBy: tx.id, + shared: subscriptionID, + }; + logger.trace(`Updating account index for the account with address ${member}.`); + await membersTable.upsert(memberData, dbTrx); + logger.debug(`Updated account index for the account with address ${member}.`); + }, + { concurrency: added.length }, + ); + logger.trace(`Added new members with subscription ID ${subscriptionID}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => { + const membersTable = await getMembersTable(); + + const removed = await membersTable.find( + { removedBy: tx.id }, + ['id', 'address', 'shared'], + dbTrx, + ); + + await membersTable.delete({ addedBy: tx.id }, dbTrx); + + await BluebirdPromise.map( + removed, + async member => { + const memberData = { + ...member, + removedBy: null, + }; + logger.trace(`Updating account index for the account with address ${member}.`); + await membersTable.upsert(memberData, dbTrx); + logger.debug(`Updated account index for the account with address ${member}.`); + }, + { concurrency: removed.length }, + ); +}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From b47ac15c6170f3ecc2d1ff29022b9444b7b03afe Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Sun, 28 May 2023 15:56:14 +0330 Subject: [PATCH 15/79] Add Collection Indexing Part01 --- .../shared/sdk/constants/eventTopics.js | 6 ++ .../shared/sdk/constants/names.js | 7 ++ .../dataService/business/collections.js | 37 ++++++++ .../shared/dataService/collection.js | 22 +++++ .../shared/dataService/subscriptions.js | 10 +- .../shared/database/schema/collections.js | 18 ++++ .../transactionProcessor/collection/create.js | 94 +++++++++++++++++++ .../transactionProcessor/collection/index.js | 6 ++ .../gateway/sources/version3/collections.js | 45 +++++++++ 9 files changed, 240 insertions(+), 5 deletions(-) create mode 100644 services/blockchain-indexer/shared/dataService/business/collections.js create mode 100644 services/blockchain-indexer/shared/dataService/collection.js create mode 100644 services/blockchain-indexer/shared/database/schema/collections.js create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/collection/index.js create mode 100644 services/gateway/sources/version3/collections.js diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index 8eb657ec0..67a4d839b 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -77,6 +77,9 @@ const { MODULE_NAME_SUBSCRIPTION, EVENT_NAME_SUBSCRIPTION_CREATED, EVENT_NAME_SUBSCRIPTION_PURCHASED, + + MODULE_NAME_COLLECTION, + EVENT_NAME_COLLECTION_CREATED, } = require('./names'); const COMMAND_EXECUTION_RESULT_TOPICS = ['transactionID']; @@ -148,6 +151,9 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { [EVENT_NAME_SUBSCRIPTION_CREATED]: ['transactionID', 'senderAddress'], [EVENT_NAME_SUBSCRIPTION_PURCHASED]: ['transactionID', 'senderAddress'], }, + [MODULE_NAME_COLLECTION]: { + [EVENT_NAME_COLLECTION_CREATED]: ['transactionID', 'senderAddress'], + }, }; module.exports = { diff --git a/services/blockchain-connector/shared/sdk/constants/names.js b/services/blockchain-connector/shared/sdk/constants/names.js index d51b60f04..67915ee03 100644 --- a/services/blockchain-connector/shared/sdk/constants/names.js +++ b/services/blockchain-connector/shared/sdk/constants/names.js @@ -90,6 +90,10 @@ const MODULE_NAME_SUBSCRIPTION = 'subscription'; const EVENT_NAME_SUBSCRIPTION_CREATED = 'subscriptionCreated'; const EVENT_NAME_SUBSCRIPTION_PURCHASED = 'subscriptionPurchased'; +// Collections +const MODULE_NAME_COLLECTION = 'collection'; +const EVENT_NAME_COLLECTION_CREATED = 'collectionCreated'; + module.exports = { MODULE_NAME_AUTH, EVENT_NAME_MULTISIGNATURE_REGISTERED, @@ -155,4 +159,7 @@ module.exports = { MODULE_NAME_SUBSCRIPTION, EVENT_NAME_SUBSCRIPTION_CREATED, EVENT_NAME_SUBSCRIPTION_PURCHASED, + + MODULE_NAME_COLLECTION, + EVENT_NAME_COLLECTION_CREATED, }; diff --git a/services/blockchain-indexer/shared/dataService/business/collections.js b/services/blockchain-indexer/shared/dataService/business/collections.js new file mode 100644 index 000000000..4206b5ef2 --- /dev/null +++ b/services/blockchain-indexer/shared/dataService/business/collections.js @@ -0,0 +1,37 @@ +const { + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); +const transactionsIndexSchema = require('../../database/schema/collection'); +const config = require('../../../config'); + +const MYSQL_ENDPOINT = config.endpoints.mysql; + +const getCollectionsIndex = () => getTableInstance( + transactionsIndexSchema.tableName, + transactionsIndexSchema, + MYSQL_ENDPOINT, +); + +const getCollections = async (params = {}) => { + const collectionTable = await getCollectionsIndex(); + + const total = await collectionsTable.count(params); + const resultSet = await collectionsTable.find( + { ...params, limit: params.limit || total }, + ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'maxMembers', 'streams'], + ); + + const result = { + data: resultSet, + meta: { + count: resultSet.length, + offset: parseInt(params.offset, 10) || 0, + total, + }, + }; + return result; +}; + +module.exports = { + getSubscriptions, +}; diff --git a/services/blockchain-indexer/shared/dataService/collection.js b/services/blockchain-indexer/shared/dataService/collection.js new file mode 100644 index 000000000..af8c10ec9 --- /dev/null +++ b/services/blockchain-indexer/shared/dataService/collection.js @@ -0,0 +1,22 @@ +const { Logger } = require('lisk-service-framework'); +const util = require('util'); + +const logger = Logger(); + +const business = require('./business'); + +const getSubscriptions = async params => { + // Store logs + if (params.subscriptionID) logger.debug(`Retrieved subscription with ID ${params.subscriptionID} from Lisk Core`); + else if (params.creatorAddress) logger.debug(`Retrieved subscription with creatorAddress: ${params.creatorAddress} from Lisk Core`); + else logger.debug(`Retrieved subscriptions with custom search: ${util.inspect(params)} from Lisk Core`); + + // Get data from server + const response = await business.getSubscriptions(params); + + return response; +}; + +module.exports = { + getSubscriptions, +}; diff --git a/services/blockchain-indexer/shared/dataService/subscriptions.js b/services/blockchain-indexer/shared/dataService/subscriptions.js index af8c10ec9..2b54a0336 100644 --- a/services/blockchain-indexer/shared/dataService/subscriptions.js +++ b/services/blockchain-indexer/shared/dataService/subscriptions.js @@ -5,14 +5,14 @@ const logger = Logger(); const business = require('./business'); -const getSubscriptions = async params => { +const getCollections = async params => { // Store logs - if (params.subscriptionID) logger.debug(`Retrieved subscription with ID ${params.subscriptionID} from Lisk Core`); - else if (params.creatorAddress) logger.debug(`Retrieved subscription with creatorAddress: ${params.creatorAddress} from Lisk Core`); - else logger.debug(`Retrieved subscriptions with custom search: ${util.inspect(params)} from Lisk Core`); + if (params.collectionID) logger.debug(`Retrieved collection with ID ${params.collectionID} from Lisk Core`); + else if (params.creatorAddress) logger.debug(`Retrieved collection with creatorAddress: ${params.creatorAddress} from Lisk Core`); + else logger.debug(`Retrieved collection with custom search: ${util.inspect(params)} from Lisk Core`); // Get data from server - const response = await business.getSubscriptions(params); + const response = await business.getCollections(params); return response; }; diff --git a/services/blockchain-indexer/shared/database/schema/collections.js b/services/blockchain-indexer/shared/database/schema/collections.js new file mode 100644 index 000000000..fc9888fcc --- /dev/null +++ b/services/blockchain-indexer/shared/database/schema/collections.js @@ -0,0 +1,18 @@ +module.exports = { + tableName: 'collections', + primaryKey: 'collectionID', + schema: { + collectionID: { type: 'string', null: true, defaultValue: null }, + name: { type: 'string', null: true, defaultValue: null }, + releaseYear: { type: 'string', null: true, defaultValue: null }, + collectionType: { type: 'uint32', null: true, defaultValue: null }, + audios: { type: 'array', null: true, defaultValue: null }, + coverSignature: { type: 'bytes', null: true, defaultValue: null }, + coverHash: { type: 'bytes', null: true, defaultValue: null }, + creatorAddress: { type: 'string', null: true, defaultValue: null }, + }, + indexes: { + creatorAddress: { type: 'string' }, + }, + purge: {}, +}; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js new file mode 100644 index 000000000..d378a281d --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js @@ -0,0 +1,94 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); + +const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const accountsTableSchema = require('../../../database/schema/accounts'); +const collectionsTableSchema = require('../../../database/schema/collection'); + +const getAccountsTable = () => getTableInstance( + accountsTableSchema.tableName, + accountsTableSchema, + MYSQL_ENDPOINT, +); + +const getCollectionsTable = () => getTableInstance( + collectionsTableSchema.tableName, + collectionsTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'create'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const accountsTable = await getAccountsTable(); + const collectionsTable = await getCllectionsTable(); + + const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); + + const account = { + address: senderAddress, + }; + + logger.trace(`Updating account index for the account with address ${account.address}.`); + await accountsTable.upsert(account, dbTrx); + logger.debug(`Updated account index for the account with address ${account.address}.`); + + logger.trace(`Indexing collections with address ${account.address}.`); + + // @todo make sure the process won't break if the event doesn't exist. e.g. do not index. + const { data: eventData } = events.find(e => e.module === 'collection' && e.name === 'collectionsCreated'); + + const collectionsNFT = { + ...eventData, + ...tx.params, + }; + + await collectionsTable.upsert(collectionsNFT, dbTrx); + logger.debug(`Indexed collection with ID ${eventData.collectionsID}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => { + const accountsTable = await getAccountsTable(); + const collectionsTable = await getCollectionsTable(); + + const oldAccount = accountsTable.find( + { address: getLisk32AddressFromPublicKey(tx.senderPublicKey) }, + dbTrx, + ); + + // Remove the validator details from the table on transaction reversal + const account = { + address: getLisk32AddressFromPublicKey(tx.senderPublicKey), + publicKey: tx.senderPublicKey, + collections: { + owned: oldAccount.collections.owned.filter(id => id !== dbTrx.id), + shared: null, + }, + }; + + logger.trace(`Updating account index for the account with address ${account.address}.`); + await accountsTable.upsert(account, dbTrx); + logger.debug(`Updated account index for the account with address ${account.address}.`); + + logger.trace(`Remove subscription entry for address ${account.address}.`); + const collectionPK = account[collectionsTableSchema.primaryKey]; + await collectionsTable.deleteByPrimaryKey(collectionPK, dbTrx); + logger.debug(`Removed subscription entry for ID ${collectionPK}.`); +}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/index.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/index.js new file mode 100644 index 000000000..0862e2581 --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/index.js @@ -0,0 +1,6 @@ +// Module specific constants +const MODULE_NAME = 'collection'; + +module.exports = { + MODULE_NAME, +}; diff --git a/services/gateway/sources/version3/collections.js b/services/gateway/sources/version3/collections.js new file mode 100644 index 000000000..c1bcbafc0 --- /dev/null +++ b/services/gateway/sources/version3/collections.js @@ -0,0 +1,45 @@ +/* + * LiskHQ/lisk-service + * Copyright © 2022 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + * + */ +const collection = require('./mappings/subscription'); + +module.exports = { + type: 'moleculer', + method: 'indexer.collection', + params: { + collectionID: '=,string', + creatorAddress: '=,string', + price: '=,string', + consumed: '=,string', + members: ['data.members', { + address: '=,string', + }], + streams: '=,string', + maxMembers: '=,number', + limit: '=,number', + offset: '=,number', + sort: '=,string', + order: '=,string', + }, + definition: { + data: ['data', subscription], + meta: { + count: '=,number', + offset: '=,number', + total: '=,number', + }, + links: {}, + }, +}; From 1ebc0dfc17781e7b3138795f99b532dbb94db98d Mon Sep 17 00:00:00 2001 From: curvesy Date: Mon, 29 May 2023 05:49:32 -0400 Subject: [PATCH 16/79] Add collection to the gateway --- .../dataService/controllers/collections.js | 31 +++++++ .../methods/dataService/modules/collection.js | 16 ++++ .../dataService/business/collections.js | 6 +- .../{collection.js => collections.js} | 10 +-- .../apis/http-version3/methods/collections.js | 54 ++++++++++++ .../apis/http-version3/swagger/apiJson.json | 4 + .../swagger/definitions/collections.json | 88 +++++++++++++++++++ .../swagger/parameters/collections.json | 19 ++++ .../gateway/sources/version3/collections.js | 33 ++----- .../sources/version3/mappings/collection.js | 22 +++++ 10 files changed, 251 insertions(+), 32 deletions(-) create mode 100644 services/blockchain-indexer/methods/dataService/controllers/collections.js create mode 100644 services/blockchain-indexer/methods/dataService/modules/collection.js rename services/blockchain-indexer/shared/dataService/{collection.js => collections.js} (52%) create mode 100644 services/gateway/apis/http-version3/methods/collections.js create mode 100644 services/gateway/apis/http-version3/swagger/definitions/collections.json create mode 100644 services/gateway/apis/http-version3/swagger/parameters/collections.json create mode 100644 services/gateway/sources/version3/mappings/collection.js diff --git a/services/blockchain-indexer/methods/dataService/controllers/collections.js b/services/blockchain-indexer/methods/dataService/controllers/collections.js new file mode 100644 index 000000000..e34ddefa8 --- /dev/null +++ b/services/blockchain-indexer/methods/dataService/controllers/collections.js @@ -0,0 +1,31 @@ +const { + HTTP: { StatusCodes: { BAD_REQUEST } }, + Exceptions: { ValidationException, InvalidParamsException }, +} = require('lisk-service-framework'); + +const dataService = require('../../../shared/dataService'); + +const getCollections = async params => { + const collections = { + data: [], + meta: {}, + }; + + try { + const response = await dataService.getCollections(params); + if (response.data) collections.data = response.data; + if (response.meta) collections.meta = response.meta; + + return collections; + } catch (err) { + let status; + if (err instanceof InvalidParamsException) status = 'INVALID_PARAMS'; + if (err instanceof ValidationException) status = BAD_REQUEST; + if (status) return { status, data: { error: err.message } }; + throw err; + } +}; + +module.exports = { + getCollections, +}; diff --git a/services/blockchain-indexer/methods/dataService/modules/collection.js b/services/blockchain-indexer/methods/dataService/modules/collection.js new file mode 100644 index 000000000..76fb7fa94 --- /dev/null +++ b/services/blockchain-indexer/methods/dataService/modules/collection.js @@ -0,0 +1,16 @@ +const { + getCollections, +} = require('../controllers/collections'); + +module.exports = [ + { + name: 'collections', + controller: getCollections, + params: { + creatorAddress: { optional: true, type: 'string' }, + collectionID: { optional: true, type: 'string' }, + limit: { optional: true, type: 'number' }, + offset: { optional: true, type: 'number' }, + }, + }, +]; diff --git a/services/blockchain-indexer/shared/dataService/business/collections.js b/services/blockchain-indexer/shared/dataService/business/collections.js index 4206b5ef2..6d315fd3a 100644 --- a/services/blockchain-indexer/shared/dataService/business/collections.js +++ b/services/blockchain-indexer/shared/dataService/business/collections.js @@ -13,12 +13,12 @@ const getCollectionsIndex = () => getTableInstance( ); const getCollections = async (params = {}) => { - const collectionTable = await getCollectionsIndex(); + const collectionsTable = await getCollectionsIndex(); const total = await collectionsTable.count(params); const resultSet = await collectionsTable.find( { ...params, limit: params.limit || total }, - ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'maxMembers', 'streams'], + ['collectionID', 'creatorAddress', 'name', 'releaseYear', 'collectionType'], ); const result = { @@ -33,5 +33,5 @@ const getCollections = async (params = {}) => { }; module.exports = { - getSubscriptions, + getCollections, }; diff --git a/services/blockchain-indexer/shared/dataService/collection.js b/services/blockchain-indexer/shared/dataService/collections.js similarity index 52% rename from services/blockchain-indexer/shared/dataService/collection.js rename to services/blockchain-indexer/shared/dataService/collections.js index af8c10ec9..6ae4b49e2 100644 --- a/services/blockchain-indexer/shared/dataService/collection.js +++ b/services/blockchain-indexer/shared/dataService/collections.js @@ -5,18 +5,18 @@ const logger = Logger(); const business = require('./business'); -const getSubscriptions = async params => { +const getCollections = async params => { // Store logs - if (params.subscriptionID) logger.debug(`Retrieved subscription with ID ${params.subscriptionID} from Lisk Core`); + if (params.collectionID) logger.debug(`Retrieved collection with ID ${params.collectionID} from Lisk Core`); else if (params.creatorAddress) logger.debug(`Retrieved subscription with creatorAddress: ${params.creatorAddress} from Lisk Core`); - else logger.debug(`Retrieved subscriptions with custom search: ${util.inspect(params)} from Lisk Core`); + else logger.debug(`Retrieved collections with custom search: ${util.inspect(params)} from Lisk Core`); // Get data from server - const response = await business.getSubscriptions(params); + const response = await business.getCollections(params); return response; }; module.exports = { - getSubscriptions, + getCollections, }; diff --git a/services/gateway/apis/http-version3/methods/collections.js b/services/gateway/apis/http-version3/methods/collections.js new file mode 100644 index 000000000..825186870 --- /dev/null +++ b/services/gateway/apis/http-version3/methods/collections.js @@ -0,0 +1,54 @@ +/* + * LiskHQ/lisk-service + * Copyright © 2022 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + * + */ + +const collectionsSource = require('../../../sources/version3/collections'); +const envelope = require('../../../sources/version3/mappings/stdEnvelope'); +const regex = require('../../../shared/regex'); +const { transformParams, response, getSwaggerDescription } = require('../../../shared/utils'); + +module.exports = { + version: '2.0', + swaggerApiPath: '/collections', + rpcMethod: 'get.collections', + tags: ['Collections'], + params: { + creatorAddress: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + collectionID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + }, + get schema() { + const collectionSchema = {}; + collectionSchema[this.swaggerApiPath] = { get: {} }; + collectionSchema[this.swaggerApiPath].get.tags = this.tags; + collectionSchema[this.swaggerApiPath].get.summary = 'Requests collections data'; + collectionSchema[this.swaggerApiPath].get.description = getSwaggerDescription({ + rpcMethod: this.rpcMethod, + description: 'Returns collections data', + }); + collectionSchema[this.swaggerApiPath].get.parameters = transformParams('collections', this.params); + collectionSchema[this.swaggerApiPath].get.responses = { + 200: { + description: 'Returns a list of collections', + schema: { + $ref: '#/definitions/collectionsWithEnvelope', + }, + }, + }; + Object.assign(collectionSchema[this.swaggerApiPath].get.responses, response); + return collectionSchema; + }, + source: collectionsSource, + envelope, +}; diff --git a/services/gateway/apis/http-version3/swagger/apiJson.json b/services/gateway/apis/http-version3/swagger/apiJson.json index 1d76fada6..b7e321606 100644 --- a/services/gateway/apis/http-version3/swagger/apiJson.json +++ b/services/gateway/apis/http-version3/swagger/apiJson.json @@ -90,6 +90,10 @@ { "name": "Subscription", "description": "Subscription module related API calls." + }, + { + "name": "Collection", + "description": "Collection module related API calls." } ], "schemes": [ diff --git a/services/gateway/apis/http-version3/swagger/definitions/collections.json b/services/gateway/apis/http-version3/swagger/definitions/collections.json new file mode 100644 index 000000000..7c6844311 --- /dev/null +++ b/services/gateway/apis/http-version3/swagger/definitions/collections.json @@ -0,0 +1,88 @@ +{ + "collection": { + "type": "object", + "required": [ + "name", + "creatorAddress", + "collectionID", + "releaseYear", + "collectionType" + ], + "properties": { + "collectionID": { + "type": "string", + "format": "id", + "example": "f9593f101c4acafc3ede650ab4c10fa2ecb59b225813eddbbb17b47e96932e9b", + "minLength": 1, + "maxLength": 64, + "description": "Unique identifier of the collection.\nDerived from the collection hash." + }, + "releaseYear": { + "type": "string", + "example": "2023" + }, + "name": { + "type": "string", + "description": "name of the collection" + }, + "collectionType": { + "type": "integer", + "example": "1" + }, + "creatorAddress": { + "type": "object", + "properties": { + "address": { + "type": "string", + "format": "address", + "example": "lskdwsyfmcko6mcd357446yatromr9vzgu7eb8y99", + "description": "The Lisk Address is the human-readable representation of a blockchain account.\nIt is 41 character long identifier that begins with `lsk`." + }, + "publicKey": { + "type": "string", + "format": "publicKey", + "example": "b1d6bc6c7edd0673f5fed0681b73de6eb70539c21278b300f07ade277e1962cd", + "description": "The public key is derived from the private key of the owner of the account.\nIt can be used to validate that the private key belongs to the owner, but not provide access to the owner's private key." + }, + "name": { + "type": "string", + "example": "genesis_84", + "description": "Delegate name" + } + } + } + } + }, + "CollectionsWithEnvelope": { + "type": "object", + "required": [ + "data", + "meta" + ], + "properties": { + "data": { + "description": "List of collections", + "type": "array", + "items": { + "$ref": "#/definitions/Collections" + } + }, + "meta": { + "$ref": "#/definitions/pagination" + } + } + }, + "serverErrorEnvelope": { + "type": "object", + "properties": { + "error": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Unable to reach a network node" + } + } + } +} diff --git a/services/gateway/apis/http-version3/swagger/parameters/collections.json b/services/gateway/apis/http-version3/swagger/parameters/collections.json new file mode 100644 index 000000000..3c80f4c56 --- /dev/null +++ b/services/gateway/apis/http-version3/swagger/parameters/collections.json @@ -0,0 +1,19 @@ +{ + "creatorAddress": { + "name": "creatorAddress", + "in": "query", + "description": "Lisk account address", + "type": "string", + "minLength": 3, + "maxLength": 41 + }, + "collectionID": { + "name": "collectionID", + "in": "query", + "description": "collection ID to query", + "type": "string", + "format": "id", + "minLength": 1, + "maxLength": 64 + } +} diff --git a/services/gateway/sources/version3/collections.js b/services/gateway/sources/version3/collections.js index c1bcbafc0..41387a8ee 100644 --- a/services/gateway/sources/version3/collections.js +++ b/services/gateway/sources/version3/collections.js @@ -1,40 +1,25 @@ -/* - * LiskHQ/lisk-service - * Copyright © 2022 Lisk Foundation - * - * See the LICENSE file at the top-level directory of this distribution - * for licensing information. - * - * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, - * no part of this software, including this file, may be copied, modified, - * propagated, or distributed except according to the terms contained in the - * LICENSE file. - * - * Removal or modification of this copyright notice is prohibited. - * - */ -const collection = require('./mappings/subscription'); + +const collection = require('./mappings/collection'); module.exports = { type: 'moleculer', method: 'indexer.collection', params: { + name: '=,string', collectionID: '=,string', creatorAddress: '=,string', - price: '=,string', - consumed: '=,string', - members: ['data.members', { - address: '=,string', - }], - streams: '=,string', - maxMembers: '=,number', + releaseYear: '=,string', + collectionType: '=,number', + // audios: ['data.audios', { + // address: '=,string', + // }], limit: '=,number', offset: '=,number', sort: '=,string', order: '=,string', }, definition: { - data: ['data', subscription], + data: ['data', collection], meta: { count: '=,number', offset: '=,number', diff --git a/services/gateway/sources/version3/mappings/collection.js b/services/gateway/sources/version3/mappings/collection.js new file mode 100644 index 000000000..f444757b8 --- /dev/null +++ b/services/gateway/sources/version3/mappings/collection.js @@ -0,0 +1,22 @@ +/* + * LiskHQ/lisk-service + * Copyright © 2022 Lisk Foundation + * + * See the LICENSE file at the top-level directory of this distribution + * for licensing information. + * + * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, + * no part of this software, including this file, may be copied, modified, + * propagated, or distributed except according to the terms contained in the + * LICENSE file. + * + * Removal or modification of this copyright notice is prohibited. + * + */ +module.exports = { + name: '=,string', + collectionID: '=,string', + creatorAddress: '=,string', + collectionType: '=,number', + releaseYear: '=,string', +}; From 4e700cdb58e35974e20ac2b04165f12845a68c49 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Tue, 30 May 2023 14:54:08 +0330 Subject: [PATCH 17/79] Replace collection data service by subscription --- .../shared/dataService/collections.js | 2 +- .../shared/dataService/subscriptions.js | 10 +++++----- .../gateway/apis/http-version3/swagger/apiJson.json | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/services/blockchain-indexer/shared/dataService/collections.js b/services/blockchain-indexer/shared/dataService/collections.js index 6ae4b49e2..06c0380ca 100644 --- a/services/blockchain-indexer/shared/dataService/collections.js +++ b/services/blockchain-indexer/shared/dataService/collections.js @@ -8,7 +8,7 @@ const business = require('./business'); const getCollections = async params => { // Store logs if (params.collectionID) logger.debug(`Retrieved collection with ID ${params.collectionID} from Lisk Core`); - else if (params.creatorAddress) logger.debug(`Retrieved subscription with creatorAddress: ${params.creatorAddress} from Lisk Core`); + else if (params.creatorAddress) logger.debug(`Retrieved collection with creatorAddress: ${params.creatorAddress} from Lisk Core`); else logger.debug(`Retrieved collections with custom search: ${util.inspect(params)} from Lisk Core`); // Get data from server diff --git a/services/blockchain-indexer/shared/dataService/subscriptions.js b/services/blockchain-indexer/shared/dataService/subscriptions.js index 2b54a0336..df99a6d0d 100644 --- a/services/blockchain-indexer/shared/dataService/subscriptions.js +++ b/services/blockchain-indexer/shared/dataService/subscriptions.js @@ -5,14 +5,14 @@ const logger = Logger(); const business = require('./business'); -const getCollections = async params => { +const getSubscriptions = async params => { // Store logs - if (params.collectionID) logger.debug(`Retrieved collection with ID ${params.collectionID} from Lisk Core`); - else if (params.creatorAddress) logger.debug(`Retrieved collection with creatorAddress: ${params.creatorAddress} from Lisk Core`); - else logger.debug(`Retrieved collection with custom search: ${util.inspect(params)} from Lisk Core`); + if (params.subscriptionID) logger.debug(`Retrieved subscription with ID ${params.subscriptionID} from Lisk Core`); + else if (params.creatorAddress) logger.debug(`Retrieved subscription with creatorAddress: ${params.creatorAddress} from Lisk Core`); + else logger.debug(`Retrieved subscription with custom search: ${util.inspect(params)} from Lisk Core`); // Get data from server - const response = await business.getCollections(params); + const response = await business.getSubscriptions(params); return response; }; diff --git a/services/gateway/apis/http-version3/swagger/apiJson.json b/services/gateway/apis/http-version3/swagger/apiJson.json index b7e321606..8311e1aeb 100644 --- a/services/gateway/apis/http-version3/swagger/apiJson.json +++ b/services/gateway/apis/http-version3/swagger/apiJson.json @@ -91,7 +91,7 @@ "name": "Subscription", "description": "Subscription module related API calls." }, - { + { "name": "Collection", "description": "Collection module related API calls." } From 5060b753e4cd3c7d3008317b3201de4a0911bdfe Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Wed, 31 May 2023 14:17:54 +0200 Subject: [PATCH 18/79] Fix databsae schema mispatch --- .../shared/database/schema/collections.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/services/blockchain-indexer/shared/database/schema/collections.js b/services/blockchain-indexer/shared/database/schema/collections.js index fc9888fcc..a4dfbd621 100644 --- a/services/blockchain-indexer/shared/database/schema/collections.js +++ b/services/blockchain-indexer/shared/database/schema/collections.js @@ -5,10 +5,9 @@ module.exports = { collectionID: { type: 'string', null: true, defaultValue: null }, name: { type: 'string', null: true, defaultValue: null }, releaseYear: { type: 'string', null: true, defaultValue: null }, - collectionType: { type: 'uint32', null: true, defaultValue: null }, - audios: { type: 'array', null: true, defaultValue: null }, - coverSignature: { type: 'bytes', null: true, defaultValue: null }, - coverHash: { type: 'bytes', null: true, defaultValue: null }, + collectionType: { type: 'integer', null: true, defaultValue: null }, + coverSignature: { type: 'string', null: true, defaultValue: null }, + coverHash: { type: 'string', null: true, defaultValue: null }, creatorAddress: { type: 'string', null: true, defaultValue: null }, }, indexes: { From 04610b163024384ffb114ac2c1a07251fc9ad217 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Wed, 31 May 2023 14:18:24 +0200 Subject: [PATCH 19/79] Fix data wiring issues --- .../shared/dataService/business/collections.js | 2 +- services/blockchain-indexer/shared/dataService/index.js | 4 ++++ .../indexer/transactionProcessor/collection/create.js | 4 ++-- .../apis/http-version3/swagger/definitions/collections.json | 6 +++--- services/gateway/sources/version3/collections.js | 6 +++--- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/services/blockchain-indexer/shared/dataService/business/collections.js b/services/blockchain-indexer/shared/dataService/business/collections.js index 6d315fd3a..66c59be29 100644 --- a/services/blockchain-indexer/shared/dataService/business/collections.js +++ b/services/blockchain-indexer/shared/dataService/business/collections.js @@ -1,7 +1,7 @@ const { MySQL: { getTableInstance }, } = require('lisk-service-framework'); -const transactionsIndexSchema = require('../../database/schema/collection'); +const transactionsIndexSchema = require('../../database/schema/collections'); const config = require('../../../config'); const MYSQL_ENDPOINT = config.endpoints.mysql; diff --git a/services/blockchain-indexer/shared/dataService/index.js b/services/blockchain-indexer/shared/dataService/index.js index aa7870f4d..82eec2636 100644 --- a/services/blockchain-indexer/shared/dataService/index.js +++ b/services/blockchain-indexer/shared/dataService/index.js @@ -94,6 +94,7 @@ const { getLegacyAccountInfo } = require('./legacy'); const { getValidator, validateBLSKey } = require('./validator'); const { getGenerators } = require('./generators'); const { getSubscriptions } = require('./subscriptions'); +const { getCollections } = require('./collections'); module.exports = { // Blocks @@ -184,4 +185,7 @@ module.exports = { // Subscriptions getSubscriptions, + + // Collections + getCollections, }; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js index d378a281d..9b9c1d5fb 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js @@ -11,7 +11,7 @@ const logger = Logger(); const MYSQL_ENDPOINT = config.endpoints.mysql; const accountsTableSchema = require('../../../database/schema/accounts'); -const collectionsTableSchema = require('../../../database/schema/collection'); +const collectionsTableSchema = require('../../../database/schema/collections'); const getAccountsTable = () => getTableInstance( accountsTableSchema.tableName, @@ -31,7 +31,7 @@ const COMMAND_NAME = 'create'; // eslint-disable-next-line no-unused-vars const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const accountsTable = await getAccountsTable(); - const collectionsTable = await getCllectionsTable(); + const collectionsTable = await getCollectionsTable(); const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); diff --git a/services/gateway/apis/http-version3/swagger/definitions/collections.json b/services/gateway/apis/http-version3/swagger/definitions/collections.json index 7c6844311..b0cf3e85d 100644 --- a/services/gateway/apis/http-version3/swagger/definitions/collections.json +++ b/services/gateway/apis/http-version3/swagger/definitions/collections.json @@ -2,11 +2,11 @@ "collection": { "type": "object", "required": [ - "name", - "creatorAddress", "collectionID", "releaseYear", - "collectionType" + "name", + "collectionType", + "creatorAddress" ], "properties": { "collectionID": { diff --git a/services/gateway/sources/version3/collections.js b/services/gateway/sources/version3/collections.js index 41387a8ee..5f7be0091 100644 --- a/services/gateway/sources/version3/collections.js +++ b/services/gateway/sources/version3/collections.js @@ -1,4 +1,3 @@ - const collection = require('./mappings/collection'); module.exports = { @@ -10,8 +9,9 @@ module.exports = { creatorAddress: '=,string', releaseYear: '=,string', collectionType: '=,number', - // audios: ['data.audios', { - // address: '=,string', + // audios: ['audios', { + // audioID: '=,string', + // name: '=,number', // }], limit: '=,number', offset: '=,number', From 306e688533bda36397a9809986623456e9a89366 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Wed, 31 May 2023 14:36:30 +0200 Subject: [PATCH 20/79] Fix event filtering mechanism --- .../shared/indexer/transactionProcessor/collection/create.js | 2 +- .../indexer/transactionProcessor/subscription/create.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js index 9b9c1d5fb..8621ddbb1 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js @@ -46,7 +46,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { logger.trace(`Indexing collections with address ${account.address}.`); // @todo make sure the process won't break if the event doesn't exist. e.g. do not index. - const { data: eventData } = events.find(e => e.module === 'collection' && e.name === 'collectionsCreated'); + const { data: eventData = {} } = events.find(e => e.module === 'collection' && e.name === 'collectionCreated'); const collectionsNFT = { ...eventData, diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js index 0247c14c3..c3deecc64 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/create.js @@ -41,7 +41,9 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { logger.debug(`Updated account index for the account with address ${senderAddress}.`); logger.trace(`Indexing subscription with address ${senderAddress}.`); - const { data: eventData } = events.find(e => e.module === 'subscription' && e.name === 'subscriptionCreated'); + + // @todo make sure the process won't break if the event doesn't exist. e.g. do not index. + const { data: eventData = {} } = events.find(e => e.module === 'subscription' && e.name === 'subscriptionCreated'); const subscriptionsNFT = { ...eventData, From c3f59bd85a9b9a7ac424c48362539266d7d78776 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Wed, 31 May 2023 14:39:17 +0200 Subject: [PATCH 21/79] Add getcolletions to the gateway known sources --- .../blockchain-indexer/shared/dataService/business/index.js | 4 ++++ services/gateway/sources/version3/collections.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/services/blockchain-indexer/shared/dataService/business/index.js b/services/blockchain-indexer/shared/dataService/business/index.js index b3d5186ad..6cf2932ed 100644 --- a/services/blockchain-indexer/shared/dataService/business/index.js +++ b/services/blockchain-indexer/shared/dataService/business/index.js @@ -95,6 +95,7 @@ const { getNetworkPeersStatistics, } = require('./network'); const { getSubscriptions } = require('./subscriptions'); +const { getCollections } = require('./collections'); module.exports = { // Generators @@ -179,4 +180,7 @@ module.exports = { // subscriptions getSubscriptions, + + // collections + getCollections, }; diff --git a/services/gateway/sources/version3/collections.js b/services/gateway/sources/version3/collections.js index 5f7be0091..4c1776a22 100644 --- a/services/gateway/sources/version3/collections.js +++ b/services/gateway/sources/version3/collections.js @@ -2,7 +2,7 @@ const collection = require('./mappings/collection'); module.exports = { type: 'moleculer', - method: 'indexer.collection', + method: 'indexer.collections', params: { name: '=,string', collectionID: '=,string', From cc8a4a3e355217e99f27aa7a2d55d0b78d667c2e Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Wed, 31 May 2023 14:45:06 +0200 Subject: [PATCH 22/79] Read the event name from constants --- .../indexer/transactionProcessor/collection/create.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js index 8621ddbb1..b703ecedc 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js @@ -12,6 +12,10 @@ const logger = Logger(); const MYSQL_ENDPOINT = config.endpoints.mysql; const accountsTableSchema = require('../../../database/schema/accounts'); const collectionsTableSchema = require('../../../database/schema/collections'); +const { + MODULE_NAME_COLLECTION, + EVENT_NAME_COLLECTION_CREATED, +} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); const getAccountsTable = () => getTableInstance( accountsTableSchema.tableName, @@ -45,8 +49,10 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { logger.trace(`Indexing collections with address ${account.address}.`); - // @todo make sure the process won't break if the event doesn't exist. e.g. do not index. - const { data: eventData = {} } = events.find(e => e.module === 'collection' && e.name === 'collectionCreated'); + const { data: eventData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_COLLECTION + && name === EVENT_NAME_COLLECTION_CREATED, + ); const collectionsNFT = { ...eventData, From df0bf7ee2bf45f0490bf243e737965f34bcb2bc6 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Wed, 31 May 2023 14:56:30 +0200 Subject: [PATCH 23/79] Update unit tests --- .../gateway/tests/constants/generateDocs.js | 31 +++++++++++++++++++ .../gateway/tests/constants/registerApi.js | 4 +++ 2 files changed, 35 insertions(+) diff --git a/services/gateway/tests/constants/generateDocs.js b/services/gateway/tests/constants/generateDocs.js index 03fd436ee..ea3c9a96a 100644 --- a/services/gateway/tests/constants/generateDocs.js +++ b/services/gateway/tests/constants/generateDocs.js @@ -407,6 +407,37 @@ const createApiDocsExpectedResponse = { }, }, }, + '/collections': { + get: { + description: 'Returns collections data\n RPC => get.collections', + parameters: [ + { + $ref: '#/parameters/creatorAddress', + }, + { + $ref: '#/parameters/collectionID', + }, + ], + responses: { + 200: { + description: 'Returns a list of collections', + schema: { + $ref: '#/definitions/collectionsWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, + }, + }, + summary: 'Requests collections data', + tags: [ + 'Collections', + ], + }, + }, '/events': { get: { tags: [ diff --git a/services/gateway/tests/constants/registerApi.js b/services/gateway/tests/constants/registerApi.js index 749ea219c..b75dde89f 100644 --- a/services/gateway/tests/constants/registerApi.js +++ b/services/gateway/tests/constants/registerApi.js @@ -24,6 +24,7 @@ const expectedResponseForRegisterHttpApi = { 'app-registry.blockchain.apps.meta.tokens', 'app-registry.blockchain.apps.meta.tokens.supported', 'indexer.blocks', + 'indexer.collections', 'indexer.events', 'fees.estimates', 'indexer.generators', @@ -59,6 +60,7 @@ const expectedResponseForRegisterHttpApi = { ], aliases: { 'GET blocks/assets': 'indexer.blocks.assets', + 'GET collections': 'indexer.collections', 'GET blockchain/apps': 'indexer.blockchain.apps', 'GET blockchain/apps/meta/list': 'app-registry.blockchain.apps.meta.list', 'GET blockchain/apps/meta': 'app-registry.blockchain.apps.meta', @@ -114,6 +116,7 @@ const expectedResponseForRegisterRpcApi = { 'app-registry.blockchain.apps.meta.tokens', 'app-registry.blockchain.apps.meta.tokens.supported', 'indexer.blocks', + 'indexer.collections', 'indexer.events', 'fees.estimates', 'indexer.generators', @@ -147,6 +150,7 @@ const expectedResponseForRegisterRpcApi = { ], aliases: { 'get.blocks.assets': 'indexer.blocks.assets', + 'get.collections': 'indexer.collections', 'get.blockchain.apps': 'indexer.blockchain.apps', 'get.blockchain.apps.meta.list': 'app-registry.blockchain.apps.meta.list', 'get.blockchain.apps.meta': 'app-registry.blockchain.apps.meta', From ff59df654cf9999a4fa70a711ace8161be5bbb92 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Fri, 2 Jun 2023 14:21:58 +0330 Subject: [PATCH 24/79] Add first modifications for CollectionAttributeSet --- .../blockchain-connector/shared/sdk/constants/eventTopics.js | 3 +++ services/blockchain-connector/shared/sdk/constants/names.js | 4 +++- .../shared/dataService/business/collections.js | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index 67a4d839b..d2501a29f 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -80,6 +80,8 @@ const { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_CREATED, + EVENT_NAME_COLLECTION_ATTRIBUTESET, + } = require('./names'); const COMMAND_EXECUTION_RESULT_TOPICS = ['transactionID']; @@ -153,6 +155,7 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { }, [MODULE_NAME_COLLECTION]: { [EVENT_NAME_COLLECTION_CREATED]: ['transactionID', 'senderAddress'], + [EVENT_NAME_COLLECTION_ATTRIBUTESET]: ['transactionID', 'senderAddress'], }, }; diff --git a/services/blockchain-connector/shared/sdk/constants/names.js b/services/blockchain-connector/shared/sdk/constants/names.js index 67915ee03..54f3470f5 100644 --- a/services/blockchain-connector/shared/sdk/constants/names.js +++ b/services/blockchain-connector/shared/sdk/constants/names.js @@ -90,9 +90,10 @@ const MODULE_NAME_SUBSCRIPTION = 'subscription'; const EVENT_NAME_SUBSCRIPTION_CREATED = 'subscriptionCreated'; const EVENT_NAME_SUBSCRIPTION_PURCHASED = 'subscriptionPurchased'; -// Collections +// Collection const MODULE_NAME_COLLECTION = 'collection'; const EVENT_NAME_COLLECTION_CREATED = 'collectionCreated'; +const EVENT_NAME_COLLECTION_ATTRIBUTESET = 'collectionAttributeSet'; module.exports = { MODULE_NAME_AUTH, @@ -162,4 +163,5 @@ module.exports = { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_CREATED, + EVENT_NAME_COLLECTION_ATTRIBUTESET, }; diff --git a/services/blockchain-indexer/shared/dataService/business/collections.js b/services/blockchain-indexer/shared/dataService/business/collections.js index 66c59be29..02c3d797c 100644 --- a/services/blockchain-indexer/shared/dataService/business/collections.js +++ b/services/blockchain-indexer/shared/dataService/business/collections.js @@ -17,7 +17,7 @@ const getCollections = async (params = {}) => { const total = await collectionsTable.count(params); const resultSet = await collectionsTable.find( - { ...params, limit: params.limit || total }, + { ...params, limit: params.limit || 10 }, ['collectionID', 'creatorAddress', 'name', 'releaseYear', 'collectionType'], ); From f5f3e5cec5ceb7bbd76a40ba65a3be7273e5b05c Mon Sep 17 00:00:00 2001 From: curvesy Date: Fri, 2 Jun 2023 16:25:15 +0330 Subject: [PATCH 25/79] Second changes for collection setAttribute indexing --- .../collection/attributeSet.js | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js new file mode 100644 index 000000000..ac60989f0 --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js @@ -0,0 +1,77 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); + +const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const accountsTableSchema = require('../../../database/schema/accounts'); +const collectionsTableSchema = require('../../../database/schema/collections'); +const { + MODULE_NAME_COLLECTION, + EVENT_NAME_COLLECTION_ATTRIBUTESET, +} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); + +const getAccountsTable = () => getTableInstance( + accountsTableSchema.tableName, + accountsTableSchema, + MYSQL_ENDPOINT, +); + +const getCollectionsTable = () => getTableInstance( + collectionsTableSchema.tableName, + collectionsTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'attributeSet'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + // const accountsTable = await getAccountsTable(); + const collectionsTable = await getCollectionsTable(); + + // const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); + + // const account = { + // address: senderAddress, + // }; + + // logger.trace(`Updating account index for the account with address ${account.address}.`); + // await accountsTable.upsert(account, dbTrx); + // logger.debug(`Updated account index for the account with address ${account.address}.`); + // logger.trace(`Indexing collections with address ${account.address}.`); + + const { data: eventData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_COLLECTION + && name === EVENT_NAME_COLLECTION_ATTRIBUTESET, + ); + + // Thanks to blockchain logic, collectionNFTs will remain in hands of it's actual owner + // and there are no possible vulnerability to change the owner of a CollectionNFT via fraud + // then we don't need to implement the logic here for preventing creatorAddress change + const collectionsNFT = { + ...eventData, + ...tx.params, + }; + + await collectionsTable.update(collectionsNFT, dbTrx); + logger.debug(`Indexed collection with ID ${eventData.collectionsID}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => { + +}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From f04baba072359ce9f6487864f02225688e5f3356 Mon Sep 17 00:00:00 2001 From: curvesy Date: Sat, 3 Jun 2023 05:04:51 +0330 Subject: [PATCH 26/79] Added updatedCollection function to applyTransactoin in SetAttribute for collection --- .../collection/attributeSet.js | 40 ++++++------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js index ac60989f0..421f5890a 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js @@ -3,26 +3,17 @@ const { MySQL: { getTableInstance }, } = require('lisk-service-framework'); -const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); - const config = require('../../../../config'); const logger = Logger(); const MYSQL_ENDPOINT = config.endpoints.mysql; -const accountsTableSchema = require('../../../database/schema/accounts'); const collectionsTableSchema = require('../../../database/schema/collections'); const { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_ATTRIBUTESET, } = require('../../../../../blockchain-connector/shared/sdk/constants/names'); -const getAccountsTable = () => getTableInstance( - accountsTableSchema.tableName, - accountsTableSchema, - MYSQL_ENDPOINT, -); - const getCollectionsTable = () => getTableInstance( collectionsTableSchema.tableName, collectionsTableSchema, @@ -30,39 +21,32 @@ const getCollectionsTable = () => getTableInstance( ); // Command specific constants -const COMMAND_NAME = 'attributeSet'; +const COMMAND_NAME = 'setAttributes'; + +const updateCollection = async (collectionsTable, collectionID, updates, dbTrx) => { + const where = { + collectionID, + }; + + await collectionsTable.update({ where, updates }, dbTrx); + logger.debug(`Updated collection with ID ${collectionID}.`); +}; // eslint-disable-next-line no-unused-vars const applyTransaction = async (blockHeader, tx, events, dbTrx) => { - // const accountsTable = await getAccountsTable(); const collectionsTable = await getCollectionsTable(); - // const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); - - // const account = { - // address: senderAddress, - // }; - - // logger.trace(`Updating account index for the account with address ${account.address}.`); - // await accountsTable.upsert(account, dbTrx); - // logger.debug(`Updated account index for the account with address ${account.address}.`); - // logger.trace(`Indexing collections with address ${account.address}.`); - const { data: eventData = {} } = events.find( ({ module, name }) => module === MODULE_NAME_COLLECTION && name === EVENT_NAME_COLLECTION_ATTRIBUTESET, ); - // Thanks to blockchain logic, collectionNFTs will remain in hands of it's actual owner - // and there are no possible vulnerability to change the owner of a CollectionNFT via fraud - // then we don't need to implement the logic here for preventing creatorAddress change - const collectionsNFT = { + const { collectionID, ...updates } = { ...eventData, ...tx.params, }; - await collectionsTable.update(collectionsNFT, dbTrx); - logger.debug(`Indexed collection with ID ${eventData.collectionsID}.`); + await updateCollection(collectionsTable, collectionID, updates, dbTrx); }; // eslint-disable-next-line no-unused-vars From 8ac7a72d71b9a16d9f92c7fbb5c4a774c490522a Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sat, 3 Jun 2023 09:18:34 +0200 Subject: [PATCH 27/79] Fix underscrore_case name definition --- .../blockchain-connector/shared/sdk/constants/eventTopics.js | 4 ++-- services/blockchain-connector/shared/sdk/constants/names.js | 4 ++-- .../indexer/transactionProcessor/collection/attributeSet.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index d2501a29f..28e7d5828 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -80,7 +80,7 @@ const { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_CREATED, - EVENT_NAME_COLLECTION_ATTRIBUTESET, + EVENT_NAME_COLLECTION_ATTRIBUTE_SET, } = require('./names'); @@ -155,7 +155,7 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { }, [MODULE_NAME_COLLECTION]: { [EVENT_NAME_COLLECTION_CREATED]: ['transactionID', 'senderAddress'], - [EVENT_NAME_COLLECTION_ATTRIBUTESET]: ['transactionID', 'senderAddress'], + [EVENT_NAME_COLLECTION_ATTRIBUTE_SET]: ['transactionID', 'senderAddress'], }, }; diff --git a/services/blockchain-connector/shared/sdk/constants/names.js b/services/blockchain-connector/shared/sdk/constants/names.js index 54f3470f5..d280f3339 100644 --- a/services/blockchain-connector/shared/sdk/constants/names.js +++ b/services/blockchain-connector/shared/sdk/constants/names.js @@ -93,7 +93,7 @@ const EVENT_NAME_SUBSCRIPTION_PURCHASED = 'subscriptionPurchased'; // Collection const MODULE_NAME_COLLECTION = 'collection'; const EVENT_NAME_COLLECTION_CREATED = 'collectionCreated'; -const EVENT_NAME_COLLECTION_ATTRIBUTESET = 'collectionAttributeSet'; +const EVENT_NAME_COLLECTION_ATTRIBUTE_SET = 'collectionAttributeSet'; module.exports = { MODULE_NAME_AUTH, @@ -163,5 +163,5 @@ module.exports = { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_CREATED, - EVENT_NAME_COLLECTION_ATTRIBUTESET, + EVENT_NAME_COLLECTION_ATTRIBUTE_SET, }; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js index 421f5890a..fdd9cf606 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js @@ -11,7 +11,7 @@ const MYSQL_ENDPOINT = config.endpoints.mysql; const collectionsTableSchema = require('../../../database/schema/collections'); const { MODULE_NAME_COLLECTION, - EVENT_NAME_COLLECTION_ATTRIBUTESET, + EVENT_NAME_COLLECTION_ATTRIBUTE_SET, } = require('../../../../../blockchain-connector/shared/sdk/constants/names'); const getCollectionsTable = () => getTableInstance( @@ -38,7 +38,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const { data: eventData = {} } = events.find( ({ module, name }) => module === MODULE_NAME_COLLECTION - && name === EVENT_NAME_COLLECTION_ATTRIBUTESET, + && name === EVENT_NAME_COLLECTION_ATTRIBUTE_SET, ); const { collectionID, ...updates } = { From 2746a5baf87198508342576c19af844cab4741e4 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sat, 3 Jun 2023 14:55:51 +0200 Subject: [PATCH 28/79] Add support for indexing audio:create transactions --- .../shared/sdk/constants/eventTopics.js | 6 + .../shared/sdk/constants/names.js | 7 ++ .../methods/dataService/controllers/audios.js | 31 +++++ .../methods/dataService/modules/audio.js | 16 +++ .../shared/dataService/audios.js | 22 ++++ .../shared/dataService/business/audios.js | 77 ++++++++++++ .../shared/dataService/business/index.js | 4 + .../shared/dataService/index.js | 4 + .../shared/database/schema/audios.js | 20 +++ .../shared/database/schema/owners.js | 15 +++ .../transactionProcessor/audio/create.js | 119 ++++++++++++++++++ .../transactionProcessor/audio/index.js | 6 + .../transactionProcessor/collection/create.js | 4 +- .../apis/http-version3/methods/audios.js | 38 ++++++ .../apis/http-version3/methods/collections.js | 16 --- .../apis/http-version3/swagger/apiJson.json | 4 + .../swagger/definitions/audios.json | 92 ++++++++++++++ .../swagger/parameters/audios.json | 19 +++ services/gateway/sources/version3/audios.js | 36 ++++++ .../sources/version3/mappings/audio.js | 17 +++ .../gateway/tests/constants/generateDocs.js | 31 +++++ .../gateway/tests/constants/registerApi.js | 4 + 22 files changed, 570 insertions(+), 18 deletions(-) create mode 100644 services/blockchain-indexer/methods/dataService/controllers/audios.js create mode 100644 services/blockchain-indexer/methods/dataService/modules/audio.js create mode 100644 services/blockchain-indexer/shared/dataService/audios.js create mode 100644 services/blockchain-indexer/shared/dataService/business/audios.js create mode 100644 services/blockchain-indexer/shared/database/schema/audios.js create mode 100644 services/blockchain-indexer/shared/database/schema/owners.js create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/audio/index.js create mode 100644 services/gateway/apis/http-version3/methods/audios.js create mode 100644 services/gateway/apis/http-version3/swagger/definitions/audios.json create mode 100644 services/gateway/apis/http-version3/swagger/parameters/audios.json create mode 100644 services/gateway/sources/version3/audios.js create mode 100644 services/gateway/sources/version3/mappings/audio.js diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index 67a4d839b..dab3a6f22 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -80,6 +80,9 @@ const { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_CREATED, + + MODULE_NAME_AUDIO, + EVENT_NAME_AUDIO_CREATED, } = require('./names'); const COMMAND_EXECUTION_RESULT_TOPICS = ['transactionID']; @@ -154,6 +157,9 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { [MODULE_NAME_COLLECTION]: { [EVENT_NAME_COLLECTION_CREATED]: ['transactionID', 'senderAddress'], }, + [MODULE_NAME_AUDIO]: { + [EVENT_NAME_AUDIO_CREATED]: ['transactionID', 'senderAddress'], + }, }; module.exports = { diff --git a/services/blockchain-connector/shared/sdk/constants/names.js b/services/blockchain-connector/shared/sdk/constants/names.js index 67915ee03..d78f985c4 100644 --- a/services/blockchain-connector/shared/sdk/constants/names.js +++ b/services/blockchain-connector/shared/sdk/constants/names.js @@ -94,6 +94,10 @@ const EVENT_NAME_SUBSCRIPTION_PURCHASED = 'subscriptionPurchased'; const MODULE_NAME_COLLECTION = 'collection'; const EVENT_NAME_COLLECTION_CREATED = 'collectionCreated'; +// Audios +const MODULE_NAME_AUDIO = 'audio'; +const EVENT_NAME_AUDIO_CREATED = 'audioCreated'; + module.exports = { MODULE_NAME_AUTH, EVENT_NAME_MULTISIGNATURE_REGISTERED, @@ -162,4 +166,7 @@ module.exports = { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_CREATED, + + MODULE_NAME_AUDIO, + EVENT_NAME_AUDIO_CREATED, }; diff --git a/services/blockchain-indexer/methods/dataService/controllers/audios.js b/services/blockchain-indexer/methods/dataService/controllers/audios.js new file mode 100644 index 000000000..fa9ce364b --- /dev/null +++ b/services/blockchain-indexer/methods/dataService/controllers/audios.js @@ -0,0 +1,31 @@ +const { + HTTP: { StatusCodes: { BAD_REQUEST } }, + Exceptions: { ValidationException, InvalidParamsException }, +} = require('lisk-service-framework'); + +const dataService = require('../../../shared/dataService'); + +const getAudios = async params => { + const audios = { + data: [], + meta: {}, + }; + + try { + const response = await dataService.getAudios(params); + if (response.data) audios.data = response.data; + if (response.meta) audios.meta = response.meta; + + return audios; + } catch (err) { + let status; + if (err instanceof InvalidParamsException) status = 'INVALID_PARAMS'; + if (err instanceof ValidationException) status = BAD_REQUEST; + if (status) return { status, data: { error: err.message } }; + throw err; + } +}; + +module.exports = { + getAudios, +}; diff --git a/services/blockchain-indexer/methods/dataService/modules/audio.js b/services/blockchain-indexer/methods/dataService/modules/audio.js new file mode 100644 index 000000000..e95dfb599 --- /dev/null +++ b/services/blockchain-indexer/methods/dataService/modules/audio.js @@ -0,0 +1,16 @@ +const { + getAudios, +} = require('../controllers/audios'); + +module.exports = [ + { + name: 'audios', + controller: getAudios, + params: { + creatorAddress: { optional: true, type: 'string' }, + audioID: { optional: true, type: 'string' }, + limit: { optional: true, type: 'number' }, + offset: { optional: true, type: 'number' }, + }, + }, +]; diff --git a/services/blockchain-indexer/shared/dataService/audios.js b/services/blockchain-indexer/shared/dataService/audios.js new file mode 100644 index 000000000..22b4bb6d8 --- /dev/null +++ b/services/blockchain-indexer/shared/dataService/audios.js @@ -0,0 +1,22 @@ +const { Logger } = require('lisk-service-framework'); +const util = require('util'); + +const logger = Logger(); + +const business = require('./business'); + +const getAudios = async params => { + // Store logs + if (params.audioID) logger.debug(`Retrieved audio with ID ${params.audioID} from Lisk Core`); + else if (params.creatorAddress) logger.debug(`Retrieved audio with creatorAddress: ${params.creatorAddress} from Lisk Core`); + else logger.debug(`Retrieved audios with custom search: ${util.inspect(params)} from Lisk Core`); + + // Get data from server + const response = await business.getAudios(params); + + return response; +}; + +module.exports = { + getAudios, +}; diff --git a/services/blockchain-indexer/shared/dataService/business/audios.js b/services/blockchain-indexer/shared/dataService/business/audios.js new file mode 100644 index 000000000..a4601f32b --- /dev/null +++ b/services/blockchain-indexer/shared/dataService/business/audios.js @@ -0,0 +1,77 @@ +const { + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); +const BluebirdPromise = require('bluebird'); + +const transactionsIndexSchema = require('../../database/schema/audios'); +const collectionsIndexSchema = require('../../database/schema/collections'); +const ownersIndexSchema = require('../../database/schema/owners'); +const config = require('../../../config'); + +const MYSQL_ENDPOINT = config.endpoints.mysql; + +const getAudiosIndex = () => getTableInstance( + transactionsIndexSchema.tableName, + transactionsIndexSchema, + MYSQL_ENDPOINT, +); + +const getCollectionsIndex = () => getTableInstance( + collectionsIndexSchema.tableName, + collectionsIndexSchema, + MYSQL_ENDPOINT, +); + +const getOwnersIndex = () => getTableInstance( + ownersIndexSchema.tableName, + ownersIndexSchema, + MYSQL_ENDPOINT, +); + +const getAudios = async (params = {}) => { + const audiosTable = await getAudiosIndex(); + const collectionsTable = await getCollectionsIndex(); + const ownersTable = await getOwnersIndex(); + + const total = await audiosTable.count(params); + const audioData = await audiosTable.find( + { ...params, limit: params.limit || total }, + ['audioID', 'creatorAddress', 'name', 'releaseYear', 'collectionID'], + ); + + const data = await BluebirdPromise.map( + audioData, + async (audio) => { + const collectionData = await collectionsTable.find( + { collectionID: audio.collectionID }, + ['name', 'collectionType', 'releaseYear'], + ); + + const ownersData = !audioData ? null : await ownersTable.find( + { ...params, limit: params.limit || total }, + ['address', 'shared', 'income'], + ); + + return { + ...audio, + collection: collectionData, + owners: ownersData, + }; + }, + { concurrency: audioData.length }, + ); + + const result = { + data, + meta: { + count: data.length, + offset: parseInt(params.offset, 10) || 0, + total, + }, + }; + return result; +}; + +module.exports = { + getAudios, +}; diff --git a/services/blockchain-indexer/shared/dataService/business/index.js b/services/blockchain-indexer/shared/dataService/business/index.js index 6cf2932ed..ff21d7467 100644 --- a/services/blockchain-indexer/shared/dataService/business/index.js +++ b/services/blockchain-indexer/shared/dataService/business/index.js @@ -96,6 +96,7 @@ const { } = require('./network'); const { getSubscriptions } = require('./subscriptions'); const { getCollections } = require('./collections'); +const { getAudios } = require('./audios'); module.exports = { // Generators @@ -183,4 +184,7 @@ module.exports = { // collections getCollections, + + // audios + getAudios, }; diff --git a/services/blockchain-indexer/shared/dataService/index.js b/services/blockchain-indexer/shared/dataService/index.js index 82eec2636..38a2926c9 100644 --- a/services/blockchain-indexer/shared/dataService/index.js +++ b/services/blockchain-indexer/shared/dataService/index.js @@ -95,6 +95,7 @@ const { getValidator, validateBLSKey } = require('./validator'); const { getGenerators } = require('./generators'); const { getSubscriptions } = require('./subscriptions'); const { getCollections } = require('./collections'); +const { getAudios } = require('./audios'); module.exports = { // Blocks @@ -188,4 +189,7 @@ module.exports = { // Collections getCollections, + + // Audios + getAudios, }; diff --git a/services/blockchain-indexer/shared/database/schema/audios.js b/services/blockchain-indexer/shared/database/schema/audios.js new file mode 100644 index 000000000..606aaef10 --- /dev/null +++ b/services/blockchain-indexer/shared/database/schema/audios.js @@ -0,0 +1,20 @@ +module.exports = { + tableName: 'audios', + primaryKey: 'audioID', + schema: { + audioID: { type: 'string' }, + name: { type: 'string', null: true, defaultValue: null }, + releaseYear: { type: 'string', null: true, defaultValue: null }, + collectionID: { type: 'string' }, + audioSignature: { type: 'string', null: true, defaultValue: null }, + coverHash: { type: 'string', null: true, defaultValue: null }, + creatorAddress: { type: 'string', null: true, defaultValue: null }, + }, + indexes: { + creatorAddress: { type: 'string' }, + }, + purge: {}, +}; + +// genre: number[]; +// fit: Buffer[]; diff --git a/services/blockchain-indexer/shared/database/schema/owners.js b/services/blockchain-indexer/shared/database/schema/owners.js new file mode 100644 index 000000000..b939c4c35 --- /dev/null +++ b/services/blockchain-indexer/shared/database/schema/owners.js @@ -0,0 +1,15 @@ +module.exports = { + tableName: 'owners', + primaryKey: 'id', + schema: { + id: { type: 'string' }, + address: { type: 'string', null: true, defaultValue: null }, + audioID: { type: 'string' }, + share: { type: 'integer', min: 1, max: 100 }, + income: { type: 'bigInteger', null: true, defaultValue: null }, + }, + indexes: { + creatorAddress: { type: 'string' }, + }, + purge: {}, +}; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js new file mode 100644 index 000000000..6f538d25b --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js @@ -0,0 +1,119 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); +const BluebirdPromise = require('bluebird'); + +const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const accountsTableSchema = require('../../../database/schema/accounts'); +const audiosTableSchema = require('../../../database/schema/audios'); +const ownersTableSchema = require('../../../database/schema/owners'); +const { + MODULE_NAME_AUDIO, + EVENT_NAME_AUDIO_CREATED, +} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); + +const getAccountsTable = () => getTableInstance( + accountsTableSchema.tableName, + accountsTableSchema, + MYSQL_ENDPOINT, +); + +const getAudiosTable = () => getTableInstance( + audiosTableSchema.tableName, + audiosTableSchema, + MYSQL_ENDPOINT, +); + +const getOwnersTable = () => getTableInstance( + ownersTableSchema.tableName, + ownersTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'create'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const accountsTable = await getAccountsTable(); + const audiosTable = await getAudiosTable(); + const ownersTable = await getOwnersTable(); + + const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); + + const account = { + address: senderAddress, + }; + + logger.trace(`Updating account index for the account with address ${account.address}.`); + await accountsTable.upsert(account, dbTrx); + logger.debug(`Updated account index for the account with address ${account.address}.`); + + const { data: eventData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_AUDIO + && name === EVENT_NAME_AUDIO_CREATED, + ); + + // Insert owners + await BluebirdPromise.map( + tx.params.owners, + async owner => { + const memberInfo = { + id: owner.address.concat(`${eventData.audioID}-${tx.nonce.toString()}`), + ...owner, + shares: 0, + }; + logger.trace(`Updating owner index for the account with address ${owner.address}.`); + await ownersTable.upsert(memberInfo, dbTrx); + logger.debug(`Updated owner index for the account with address ${owner.address}.`); + return true; + }, + { concurrency: tx.params.members.length }, + ); + + logger.trace(`Updating owners index for the audio with audioID ${account.address}.`); + await accountsTable.upsert(account, dbTrx); + logger.debug(`Updated account index for the account with address ${account.address}.`); + + logger.trace(`Indexing audios with address ${account.address}.`); + + const audiosNFT = { + ...eventData, + ...tx.params, + }; + + await audiosTable.upsert(audiosNFT, dbTrx); + logger.debug(`Indexed audio with ID ${eventData.audiosID}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => { + const audiosTable = await getAudiosTable(); + const ownersTable = await getOwnersTable(); + + const { data: eventData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_AUDIO + && name === EVENT_NAME_AUDIO_CREATED, + ); + + logger.trace(`Deleting owners corresponding the audio ID ${eventData.audioID}.`); + await ownersTable.delete({ audioID: eventData.audioID }, dbTrx); + logger.trace(`Deleted owners corresponding the audio ID ${eventData.audioID}.`); + + logger.trace(`Removing audio entry for ID ${eventData.audioID}.`); + await audiosTable.deleteByPrimaryKey(eventData.audioID, dbTrx); + logger.debug(`Removed audio entry for ID ${eventData.audioID}.`); +}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/index.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/index.js new file mode 100644 index 000000000..d316851af --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/index.js @@ -0,0 +1,6 @@ +// Module specific constants +const MODULE_NAME = 'audio'; + +module.exports = { + MODULE_NAME, +}; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js index b703ecedc..5f3accb95 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/create.js @@ -87,10 +87,10 @@ const revertTransaction = async (blockHeader, tx, events, dbTrx) => { await accountsTable.upsert(account, dbTrx); logger.debug(`Updated account index for the account with address ${account.address}.`); - logger.trace(`Remove subscription entry for address ${account.address}.`); + logger.trace(`Remove collection entry for address ${account.address}.`); const collectionPK = account[collectionsTableSchema.primaryKey]; await collectionsTable.deleteByPrimaryKey(collectionPK, dbTrx); - logger.debug(`Removed subscription entry for ID ${collectionPK}.`); + logger.debug(`Removed collection entry for ID ${collectionPK}.`); }; module.exports = { diff --git a/services/gateway/apis/http-version3/methods/audios.js b/services/gateway/apis/http-version3/methods/audios.js new file mode 100644 index 000000000..56a054bc9 --- /dev/null +++ b/services/gateway/apis/http-version3/methods/audios.js @@ -0,0 +1,38 @@ +const audiosSource = require('../../../sources/version3/audios'); +const envelope = require('../../../sources/version3/mappings/stdEnvelope'); +const regex = require('../../../shared/regex'); +const { transformParams, response, getSwaggerDescription } = require('../../../shared/utils'); + +module.exports = { + version: '2.0', + swaggerApiPath: '/audios', + rpcMethod: 'get.audios', + tags: ['Audios'], + params: { + creatorAddress: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + audioID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + }, + get schema() { + const audioSchema = {}; + audioSchema[this.swaggerApiPath] = { get: {} }; + audioSchema[this.swaggerApiPath].get.tags = this.tags; + audioSchema[this.swaggerApiPath].get.summary = 'Requests audios data'; + audioSchema[this.swaggerApiPath].get.description = getSwaggerDescription({ + rpcMethod: this.rpcMethod, + description: 'Returns audios data', + }); + audioSchema[this.swaggerApiPath].get.parameters = transformParams('audios', this.params); + audioSchema[this.swaggerApiPath].get.responses = { + 200: { + description: 'Returns a list of audios', + schema: { + $ref: '#/definitions/audiosWithEnvelope', + }, + }, + }; + Object.assign(audioSchema[this.swaggerApiPath].get.responses, response); + return audioSchema; + }, + source: audiosSource, + envelope, +}; diff --git a/services/gateway/apis/http-version3/methods/collections.js b/services/gateway/apis/http-version3/methods/collections.js index 825186870..9367c2932 100644 --- a/services/gateway/apis/http-version3/methods/collections.js +++ b/services/gateway/apis/http-version3/methods/collections.js @@ -1,19 +1,3 @@ -/* - * LiskHQ/lisk-service - * Copyright © 2022 Lisk Foundation - * - * See the LICENSE file at the top-level directory of this distribution - * for licensing information. - * - * Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation, - * no part of this software, including this file, may be copied, modified, - * propagated, or distributed except according to the terms contained in the - * LICENSE file. - * - * Removal or modification of this copyright notice is prohibited. - * - */ - const collectionsSource = require('../../../sources/version3/collections'); const envelope = require('../../../sources/version3/mappings/stdEnvelope'); const regex = require('../../../shared/regex'); diff --git a/services/gateway/apis/http-version3/swagger/apiJson.json b/services/gateway/apis/http-version3/swagger/apiJson.json index 8311e1aeb..d93731cfc 100644 --- a/services/gateway/apis/http-version3/swagger/apiJson.json +++ b/services/gateway/apis/http-version3/swagger/apiJson.json @@ -94,6 +94,10 @@ { "name": "Collection", "description": "Collection module related API calls." + }, + { + "name": "Audio", + "description": "Audio module related API calls." } ], "schemes": [ diff --git a/services/gateway/apis/http-version3/swagger/definitions/audios.json b/services/gateway/apis/http-version3/swagger/definitions/audios.json new file mode 100644 index 000000000..3e081872d --- /dev/null +++ b/services/gateway/apis/http-version3/swagger/definitions/audios.json @@ -0,0 +1,92 @@ +{ + "audio": { + "type": "object", + "required": [ + "audioID", + "name", + "releaseYear", + "collectionID", + "creatorAddress" + ], + "properties": { + "audioID": { + "type": "string", + "format": "id", + "example": "f9593f101c4acafc3ede650ab4c10fa2ecb59b225813eddbbb17b47e96932e9b", + "minLength": 1, + "maxLength": 64, + "description": "Unique identifier of the audio.\nDerived from the audio hash." + }, + "name": { + "type": "string", + "description": "name of the audio" + }, + "releaseYear": { + "type": "string", + "example": "2023" + }, + "collectionID": { + "type": "string", + "format": "id", + "example": "f9593f101c4acafc3ede650ab4c10fa2ecb59b225813eddbbb17b47e96932e9b", + "minLength": 1, + "maxLength": 64, + "description": "Unique identifier of the collection to which the current audio belongs." + }, + "creatorAddress": { + "type": "object", + "properties": { + "address": { + "type": "string", + "format": "address", + "example": "lskdwsyfmcko6mcd357446yatromr9vzgu7eb8y99", + "description": "The Lisk Address is the human-readable representation of a blockchain account.\nIt is 41 character long identifier that begins with `lsk`." + }, + "publicKey": { + "type": "string", + "format": "publicKey", + "example": "b1d6bc6c7edd0673f5fed0681b73de6eb70539c21278b300f07ade277e1962cd", + "description": "The public key is derived from the private key of the owner of the account.\nIt can be used to validate that the private key belongs to the owner, but not provide access to the owner's private key." + }, + "name": { + "type": "string", + "example": "genesis_84", + "description": "Delegate name" + } + } + } + } + }, + "AudiosWithEnvelope": { + "type": "object", + "required": [ + "data", + "meta" + ], + "properties": { + "data": { + "description": "List of audios", + "type": "array", + "items": { + "$ref": "#/definitions/Audios" + } + }, + "meta": { + "$ref": "#/definitions/pagination" + } + } + }, + "serverErrorEnvelope": { + "type": "object", + "properties": { + "error": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Unable to reach a network node" + } + } + } +} diff --git a/services/gateway/apis/http-version3/swagger/parameters/audios.json b/services/gateway/apis/http-version3/swagger/parameters/audios.json new file mode 100644 index 000000000..3fe410c6f --- /dev/null +++ b/services/gateway/apis/http-version3/swagger/parameters/audios.json @@ -0,0 +1,19 @@ +{ + "creatorAddress": { + "name": "creatorAddress", + "in": "query", + "description": "Lisk account address", + "type": "string", + "minLength": 3, + "maxLength": 41 + }, + "audioID": { + "name": "audioID", + "in": "query", + "description": "audio ID to query", + "type": "string", + "format": "id", + "minLength": 1, + "maxLength": 64 + } +} diff --git a/services/gateway/sources/version3/audios.js b/services/gateway/sources/version3/audios.js new file mode 100644 index 000000000..52cf24c2f --- /dev/null +++ b/services/gateway/sources/version3/audios.js @@ -0,0 +1,36 @@ +const audio = require('./mappings/audio'); + +module.exports = { + type: 'moleculer', + method: 'indexer.audios', + params: { + name: '=,string', + audioID: '=,string', + creatorAddress: '=,string', + releaseYear: '=,string', + collectionID: '=,string', + audios: ['collection', { + collectionType: '=,string', + releaseYear: '=,number', + name: '=,number', + }], + owners: ['owners', { + address: '=,string', + shares: '=,number', + income: '=,number', + }], + limit: '=,number', + offset: '=,number', + sort: '=,string', + order: '=,string', + }, + definition: { + data: ['data', audio], + meta: { + count: '=,number', + offset: '=,number', + total: '=,number', + }, + links: {}, + }, +}; diff --git a/services/gateway/sources/version3/mappings/audio.js b/services/gateway/sources/version3/mappings/audio.js new file mode 100644 index 000000000..97bac17a4 --- /dev/null +++ b/services/gateway/sources/version3/mappings/audio.js @@ -0,0 +1,17 @@ +module.exports = { + name: '=,string', + releaseYear: '=,string', + audioID: '=,string', + collectionID: '=,string', + creatorAddress: '=,string', + audios: ['collection', { + collectionType: '=,string', + releaseYear: '=,number', + name: '=,number', + }], + owners: ['owners', { + address: '=,string', + shares: '=,number', + income: '=,number', + }], +}; diff --git a/services/gateway/tests/constants/generateDocs.js b/services/gateway/tests/constants/generateDocs.js index ea3c9a96a..d86a223fd 100644 --- a/services/gateway/tests/constants/generateDocs.js +++ b/services/gateway/tests/constants/generateDocs.js @@ -14,6 +14,37 @@ * */ const createApiDocsExpectedResponse = { + '/audios': { + get: { + description: 'Returns audios data\n RPC => get.audios', + parameters: [ + { + $ref: '#/parameters/creatorAddress', + }, + { + $ref: '#/parameters/audioID', + }, + ], + responses: { + 200: { + description: 'Returns a list of audios', + schema: { + $ref: '#/definitions/audiosWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, + }, + }, + summary: 'Requests audios data', + tags: [ + 'Audios', + ], + }, + }, '/blocks/assets': { get: { tags: [ diff --git a/services/gateway/tests/constants/registerApi.js b/services/gateway/tests/constants/registerApi.js index b75dde89f..a6dda7c70 100644 --- a/services/gateway/tests/constants/registerApi.js +++ b/services/gateway/tests/constants/registerApi.js @@ -25,6 +25,7 @@ const expectedResponseForRegisterHttpApi = { 'app-registry.blockchain.apps.meta.tokens.supported', 'indexer.blocks', 'indexer.collections', + 'indexer.audios', 'indexer.events', 'fees.estimates', 'indexer.generators', @@ -61,6 +62,7 @@ const expectedResponseForRegisterHttpApi = { aliases: { 'GET blocks/assets': 'indexer.blocks.assets', 'GET collections': 'indexer.collections', + 'GET audios': 'indexer.audios', 'GET blockchain/apps': 'indexer.blockchain.apps', 'GET blockchain/apps/meta/list': 'app-registry.blockchain.apps.meta.list', 'GET blockchain/apps/meta': 'app-registry.blockchain.apps.meta', @@ -117,6 +119,7 @@ const expectedResponseForRegisterRpcApi = { 'app-registry.blockchain.apps.meta.tokens.supported', 'indexer.blocks', 'indexer.collections', + 'indexer.audios', 'indexer.events', 'fees.estimates', 'indexer.generators', @@ -151,6 +154,7 @@ const expectedResponseForRegisterRpcApi = { aliases: { 'get.blocks.assets': 'indexer.blocks.assets', 'get.collections': 'indexer.collections', + 'get.audios': 'indexer.audios', 'get.blockchain.apps': 'indexer.blockchain.apps', 'get.blockchain.apps.meta.list': 'app-registry.blockchain.apps.meta.list', 'get.blockchain.apps.meta': 'app-registry.blockchain.apps.meta', From 21a097e6b7f2a237d2ba5749a6590f0ea293b1d1 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sun, 4 Jun 2023 09:49:17 +0200 Subject: [PATCH 29/79] Add featurng artist list --- .../shared/dataService/business/audios.js | 18 ++++++++++-- .../shared/database/schema/fits.js | 13 +++++++++ .../shared/database/schema/owners.js | 4 +-- .../transactionProcessor/audio/create.js | 28 ++++++++++++++++++- .../swagger/definitions/audios.json | 28 ++++++++++++++++++- services/gateway/sources/version3/audios.js | 5 ++++ .../gateway/tests/constants/registerApi.js | 4 +-- 7 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 services/blockchain-indexer/shared/database/schema/fits.js diff --git a/services/blockchain-indexer/shared/dataService/business/audios.js b/services/blockchain-indexer/shared/dataService/business/audios.js index a4601f32b..ee86f1ee9 100644 --- a/services/blockchain-indexer/shared/dataService/business/audios.js +++ b/services/blockchain-indexer/shared/dataService/business/audios.js @@ -6,6 +6,7 @@ const BluebirdPromise = require('bluebird'); const transactionsIndexSchema = require('../../database/schema/audios'); const collectionsIndexSchema = require('../../database/schema/collections'); const ownersIndexSchema = require('../../database/schema/owners'); +const fitsIndexSchema = require('../../database/schema/fits'); const config = require('../../../config'); const MYSQL_ENDPOINT = config.endpoints.mysql; @@ -28,10 +29,17 @@ const getOwnersIndex = () => getTableInstance( MYSQL_ENDPOINT, ); +const getFitsIndex = () => getTableInstance( + fitsIndexSchema.tableName, + fitsIndexSchema, + MYSQL_ENDPOINT, +); + const getAudios = async (params = {}) => { const audiosTable = await getAudiosIndex(); const collectionsTable = await getCollectionsIndex(); const ownersTable = await getOwnersIndex(); + const fitsTable = await getFitsIndex(); const total = await audiosTable.count(params); const audioData = await audiosTable.find( @@ -47,8 +55,13 @@ const getAudios = async (params = {}) => { ['name', 'collectionType', 'releaseYear'], ); - const ownersData = !audioData ? null : await ownersTable.find( - { ...params, limit: params.limit || total }, + const ownersData = await ownersTable.find( + { audioID: audio.audioID }, + ['address', 'shared', 'income'], + ); + + const fitData = await fitsTable.find( + { audioID: audio.audioID }, ['address', 'shared', 'income'], ); @@ -56,6 +69,7 @@ const getAudios = async (params = {}) => { ...audio, collection: collectionData, owners: ownersData, + fit: fitData, }; }, { concurrency: audioData.length }, diff --git a/services/blockchain-indexer/shared/database/schema/fits.js b/services/blockchain-indexer/shared/database/schema/fits.js new file mode 100644 index 000000000..5a593f671 --- /dev/null +++ b/services/blockchain-indexer/shared/database/schema/fits.js @@ -0,0 +1,13 @@ +module.exports = { + tableName: 'owners', + primaryKey: ['address', 'audioID'], + schema: { + address: { type: 'string', null: true, defaultValue: null }, + audioID: { type: 'string' }, + role: { type: 'string', null: true, defaultValue: null }, + }, + indexes: { + creatorAddress: { type: 'string' }, + }, + purge: {}, +}; diff --git a/services/blockchain-indexer/shared/database/schema/owners.js b/services/blockchain-indexer/shared/database/schema/owners.js index b939c4c35..909ab3443 100644 --- a/services/blockchain-indexer/shared/database/schema/owners.js +++ b/services/blockchain-indexer/shared/database/schema/owners.js @@ -1,10 +1,10 @@ module.exports = { tableName: 'owners', - primaryKey: 'id', + primaryKey: ['address', 'audioID', 'nonce'], schema: { - id: { type: 'string' }, address: { type: 'string', null: true, defaultValue: null }, audioID: { type: 'string' }, + nonce: { type: 'bigInteger', min: 0 }, share: { type: 'integer', min: 1, max: 100 }, income: { type: 'bigInteger', null: true, defaultValue: null }, }, diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js index 6f538d25b..db6660625 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js @@ -14,6 +14,7 @@ const MYSQL_ENDPOINT = config.endpoints.mysql; const accountsTableSchema = require('../../../database/schema/accounts'); const audiosTableSchema = require('../../../database/schema/audios'); const ownersTableSchema = require('../../../database/schema/owners'); +const fitsTableSchema = require('../../../database/schema/fits'); const { MODULE_NAME_AUDIO, EVENT_NAME_AUDIO_CREATED, @@ -37,6 +38,12 @@ const getOwnersTable = () => getTableInstance( MYSQL_ENDPOINT, ); +const getFitsTable = () => getTableInstance( + fitsTableSchema.tableName, + fitsTableSchema, + MYSQL_ENDPOINT, +); + // Command specific constants const COMMAND_NAME = 'create'; @@ -45,6 +52,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const accountsTable = await getAccountsTable(); const audiosTable = await getAudiosTable(); const ownersTable = await getOwnersTable(); + const fitsTable = await getFitsTable(); const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); @@ -66,8 +74,9 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { tx.params.owners, async owner => { const memberInfo = { - id: owner.address.concat(`${eventData.audioID}-${tx.nonce.toString()}`), ...owner, + audioID: eventData.audioID, + nonce: eventData.nonce, shares: 0, }; logger.trace(`Updating owner index for the account with address ${owner.address}.`); @@ -78,6 +87,23 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { { concurrency: tx.params.members.length }, ); + // Insert fits + await BluebirdPromise.map( + tx.params.fit, + async fit => { + const fitInfo = { + address: fit, + role: 'co-artist', // TODO: get role from tx.params.fit + audioID: eventData.audioID, + }; + logger.trace(`Updating fits index for the account with address ${fit}.`); + await fitsTable.upsert(fitInfo, dbTrx); + logger.debug(`Updated fits index for the account with address ${fit}.`); + return true; + }, + { concurrency: tx.params.fit.length }, + ); + logger.trace(`Updating owners index for the audio with audioID ${account.address}.`); await accountsTable.upsert(account, dbTrx); logger.debug(`Updated account index for the account with address ${account.address}.`); diff --git a/services/gateway/apis/http-version3/swagger/definitions/audios.json b/services/gateway/apis/http-version3/swagger/definitions/audios.json index 3e081872d..ab64ba989 100644 --- a/services/gateway/apis/http-version3/swagger/definitions/audios.json +++ b/services/gateway/apis/http-version3/swagger/definitions/audios.json @@ -6,7 +6,8 @@ "name", "releaseYear", "collectionID", - "creatorAddress" + "creatorAddress", + "fit" ], "properties": { "audioID": { @@ -54,6 +55,31 @@ "description": "Delegate name" } } + }, + "fit": { + "type": "object", + "required": [ + "address", + "name", + "role" + ], + "properties": { + "address": { + "type": "string", + "example": "lskdwsyfmcko6mcd357446yatromr9vzgu7eb8y99", + "description": "Address of the block generator." + }, + "name": { + "type": "string", + "example": "genesis_3", + "description": "Name of the block generator." + }, + "role": { + "type": "string", + "example": "guitarist", + "description": "Role of the artist in creation of the audio." + } + } } } }, diff --git a/services/gateway/sources/version3/audios.js b/services/gateway/sources/version3/audios.js index 52cf24c2f..178556a37 100644 --- a/services/gateway/sources/version3/audios.js +++ b/services/gateway/sources/version3/audios.js @@ -19,6 +19,11 @@ module.exports = { shares: '=,number', income: '=,number', }], + fit: ['fit', { + address: '=,string', + name: '=,string', + role: '=,string', + }], limit: '=,number', offset: '=,number', sort: '=,string', diff --git a/services/gateway/tests/constants/registerApi.js b/services/gateway/tests/constants/registerApi.js index a6dda7c70..8e72f877d 100644 --- a/services/gateway/tests/constants/registerApi.js +++ b/services/gateway/tests/constants/registerApi.js @@ -16,6 +16,7 @@ // TODO: Expected response for registerApi method should be dynamically constructed const expectedResponseForRegisterHttpApi = { whitelist: [ + 'indexer.audios', 'indexer.blocks.assets', 'indexer.blockchain.apps', 'app-registry.blockchain.apps.meta.list', @@ -25,7 +26,6 @@ const expectedResponseForRegisterHttpApi = { 'app-registry.blockchain.apps.meta.tokens.supported', 'indexer.blocks', 'indexer.collections', - 'indexer.audios', 'indexer.events', 'fees.estimates', 'indexer.generators', @@ -110,6 +110,7 @@ const expectedResponseForRegisterRpcApi = { events: { request: { whitelist: [ + 'indexer.audios', 'indexer.blocks.assets', 'indexer.blockchain.apps', 'app-registry.blockchain.apps.meta.list', @@ -119,7 +120,6 @@ const expectedResponseForRegisterRpcApi = { 'app-registry.blockchain.apps.meta.tokens.supported', 'indexer.blocks', 'indexer.collections', - 'indexer.audios', 'indexer.events', 'fees.estimates', 'indexer.generators', From 60ac7c797081e76104f871c3aeb0e0c48a90b0fe Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sun, 4 Jun 2023 10:40:54 +0200 Subject: [PATCH 30/79] Fix data wiring for audio --- .../shared/dataService/business/audios.js | 6 +++--- .../blockchain-indexer/shared/database/schema/fits.js | 2 +- .../shared/database/schema/owners.js | 2 +- .../indexer/transactionProcessor/audio/create.js | 4 ++-- services/gateway/sources/version3/audios.js | 4 ++-- services/gateway/sources/version3/mappings/audio.js | 10 +++++++--- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/services/blockchain-indexer/shared/dataService/business/audios.js b/services/blockchain-indexer/shared/dataService/business/audios.js index ee86f1ee9..841b0f380 100644 --- a/services/blockchain-indexer/shared/dataService/business/audios.js +++ b/services/blockchain-indexer/shared/dataService/business/audios.js @@ -57,17 +57,17 @@ const getAudios = async (params = {}) => { const ownersData = await ownersTable.find( { audioID: audio.audioID }, - ['address', 'shared', 'income'], + ['address', 'share', 'income'], ); const fitData = await fitsTable.find( { audioID: audio.audioID }, - ['address', 'shared', 'income'], + ['address', 'role'], ); return { ...audio, - collection: collectionData, + collection: collectionData.length ? collectionData[0] : {}, owners: ownersData, fit: fitData, }; diff --git a/services/blockchain-indexer/shared/database/schema/fits.js b/services/blockchain-indexer/shared/database/schema/fits.js index 5a593f671..0503930af 100644 --- a/services/blockchain-indexer/shared/database/schema/fits.js +++ b/services/blockchain-indexer/shared/database/schema/fits.js @@ -1,5 +1,5 @@ module.exports = { - tableName: 'owners', + tableName: 'fits', primaryKey: ['address', 'audioID'], schema: { address: { type: 'string', null: true, defaultValue: null }, diff --git a/services/blockchain-indexer/shared/database/schema/owners.js b/services/blockchain-indexer/shared/database/schema/owners.js index 909ab3443..ce2145cbd 100644 --- a/services/blockchain-indexer/shared/database/schema/owners.js +++ b/services/blockchain-indexer/shared/database/schema/owners.js @@ -6,7 +6,7 @@ module.exports = { audioID: { type: 'string' }, nonce: { type: 'bigInteger', min: 0 }, share: { type: 'integer', min: 1, max: 100 }, - income: { type: 'bigInteger', null: true, defaultValue: null }, + income: { type: 'bigInteger', null: true, defaultValue: 0 }, }, indexes: { creatorAddress: { type: 'string' }, diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js index db6660625..a7a308126 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js @@ -76,7 +76,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const memberInfo = { ...owner, audioID: eventData.audioID, - nonce: eventData.nonce, + nonce: tx.nonce, shares: 0, }; logger.trace(`Updating owner index for the account with address ${owner.address}.`); @@ -84,7 +84,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { logger.debug(`Updated owner index for the account with address ${owner.address}.`); return true; }, - { concurrency: tx.params.members.length }, + { concurrency: tx.params.owners.length }, ); // Insert fits diff --git a/services/gateway/sources/version3/audios.js b/services/gateway/sources/version3/audios.js index 178556a37..834c3a8cd 100644 --- a/services/gateway/sources/version3/audios.js +++ b/services/gateway/sources/version3/audios.js @@ -9,11 +9,11 @@ module.exports = { creatorAddress: '=,string', releaseYear: '=,string', collectionID: '=,string', - audios: ['collection', { + collection: { collectionType: '=,string', releaseYear: '=,number', name: '=,number', - }], + }, owners: ['owners', { address: '=,string', shares: '=,number', diff --git a/services/gateway/sources/version3/mappings/audio.js b/services/gateway/sources/version3/mappings/audio.js index 97bac17a4..10b44cabf 100644 --- a/services/gateway/sources/version3/mappings/audio.js +++ b/services/gateway/sources/version3/mappings/audio.js @@ -4,14 +4,18 @@ module.exports = { audioID: '=,string', collectionID: '=,string', creatorAddress: '=,string', - audios: ['collection', { + collection: { collectionType: '=,string', releaseYear: '=,number', - name: '=,number', - }], + name: '=,string', + }, owners: ['owners', { address: '=,string', shares: '=,number', income: '=,number', }], + fit: ['fit', { + address: '=,string', + role: '=,string', + }], }; From 11fc3a30b11aea867adf003d3f6ff0f2bd370a5e Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sun, 4 Jun 2023 10:52:22 +0200 Subject: [PATCH 31/79] Fix creatorAddress param pattern --- services/gateway/apis/http-version3/methods/audios.js | 2 +- services/gateway/apis/http-version3/methods/collections.js | 2 +- services/gateway/apis/http-version3/methods/subscriptions.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/services/gateway/apis/http-version3/methods/audios.js b/services/gateway/apis/http-version3/methods/audios.js index 56a054bc9..896f08588 100644 --- a/services/gateway/apis/http-version3/methods/audios.js +++ b/services/gateway/apis/http-version3/methods/audios.js @@ -9,7 +9,7 @@ module.exports = { rpcMethod: 'get.audios', tags: ['Audios'], params: { - creatorAddress: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + creatorAddress: { optional: true, type: 'string', min: 3, max: 41, pattern: regex.ADDRESS_LISK32 }, audioID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, }, get schema() { diff --git a/services/gateway/apis/http-version3/methods/collections.js b/services/gateway/apis/http-version3/methods/collections.js index 9367c2932..8dc637022 100644 --- a/services/gateway/apis/http-version3/methods/collections.js +++ b/services/gateway/apis/http-version3/methods/collections.js @@ -9,7 +9,7 @@ module.exports = { rpcMethod: 'get.collections', tags: ['Collections'], params: { - creatorAddress: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + creatorAddress: { optional: true, type: 'string', min: 3, max: 41, pattern: regex.ADDRESS_LISK32 }, collectionID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, }, get schema() { diff --git a/services/gateway/apis/http-version3/methods/subscriptions.js b/services/gateway/apis/http-version3/methods/subscriptions.js index f0a4c1fd5..d3eb0d8ed 100644 --- a/services/gateway/apis/http-version3/methods/subscriptions.js +++ b/services/gateway/apis/http-version3/methods/subscriptions.js @@ -25,7 +25,7 @@ module.exports = { rpcMethod: 'get.subscriptions', tags: ['Subscriptions'], params: { - creatorAddress: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + creatorAddress: { optional: true, type: 'string', min: 3, max: 41, pattern: regex.ADDRESS_LISK32 }, subscriptionID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, }, get schema() { From 8e108ca96eccedfd2f287bc37f52a5921bf0aa71 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sun, 4 Jun 2023 10:58:59 +0200 Subject: [PATCH 32/79] Fix variable name to match the entity --- .../shared/indexer/transactionProcessor/audio/create.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js index a7a308126..e345f97b4 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js @@ -73,14 +73,14 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { await BluebirdPromise.map( tx.params.owners, async owner => { - const memberInfo = { + const ownerInfo = { ...owner, audioID: eventData.audioID, nonce: tx.nonce, shares: 0, }; logger.trace(`Updating owner index for the account with address ${owner.address}.`); - await ownersTable.upsert(memberInfo, dbTrx); + await ownersTable.upsert(ownerInfo, dbTrx); logger.debug(`Updated owner index for the account with address ${owner.address}.`); return true; }, From cc6762b20c9306ad6a0fda8974106e7b0d8ab192 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sun, 4 Jun 2023 12:30:17 +0200 Subject: [PATCH 33/79] Add support for audio:setAttribute transactions --- .../audio/setAttributes.js | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js new file mode 100644 index 000000000..6812d1f15 --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js @@ -0,0 +1,75 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); +const BluebirdPromise = require('bluebird'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const audiosTableSchema = require('../../../database/schema/audios'); +const fitsTableSchema = require('../../../database/schema/fits'); + +const getAudiosTable = () => getTableInstance( + audiosTableSchema.tableName, + audiosTableSchema, + MYSQL_ENDPOINT, +); + +const getFitsTable = () => getTableInstance( + fitsTableSchema.tableName, + fitsTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'create'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const audiosTable = await getAudiosTable(); + const fitsTable = await getFitsTable(); + + const audio = await audiosTable.find( + { audioID: tx.params.audioID }, + ['name', 'releaseYear', 'collectionID', 'creatorAddress'], + dbTrx, + ); + + // Insert fits + await BluebirdPromise.map( + tx.params.fit, + async fit => { + const fitInfo = { + address: fit, + role: 'co-artist', // TODO: get role from tx.params.fit + audioID: tx.params.audioID, + }; + logger.trace(`Updating fits index for the account with address ${fit}.`); + await fitsTable.upsert(fitInfo, dbTrx); + logger.debug(`Updated fits index for the account with address ${fit}.`); + return true; + }, + { concurrency: tx.params.fit.length }, + ); + + logger.trace(`Updating audio with ID ${tx.params.audioID}.`); + const audiosNFT = { + ...audio, + ...tx.params, + }; + + await audiosTable.upsert(audiosNFT, dbTrx); + logger.debug(`Updated audio with ID ${tx.params.audioID}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => {}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From 952471a680a557563be4c9cebe43eddbfd6b7ad1 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Sun, 4 Jun 2023 21:12:18 +0330 Subject: [PATCH 34/79] Change the name of AttributeSet.js to SetAttribute.js in order to correspond the command name --- .../collection/setAttributes.js | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/collection/setAttributes.js diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/setAttributes.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/setAttributes.js new file mode 100644 index 000000000..fdd9cf606 --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/setAttributes.js @@ -0,0 +1,61 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const collectionsTableSchema = require('../../../database/schema/collections'); +const { + MODULE_NAME_COLLECTION, + EVENT_NAME_COLLECTION_ATTRIBUTE_SET, +} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); + +const getCollectionsTable = () => getTableInstance( + collectionsTableSchema.tableName, + collectionsTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'setAttributes'; + +const updateCollection = async (collectionsTable, collectionID, updates, dbTrx) => { + const where = { + collectionID, + }; + + await collectionsTable.update({ where, updates }, dbTrx); + logger.debug(`Updated collection with ID ${collectionID}.`); +}; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const collectionsTable = await getCollectionsTable(); + + const { data: eventData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_COLLECTION + && name === EVENT_NAME_COLLECTION_ATTRIBUTE_SET, + ); + + const { collectionID, ...updates } = { + ...eventData, + ...tx.params, + }; + + await updateCollection(collectionsTable, collectionID, updates, dbTrx); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => { + +}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From a10770e618be80662275845d810f476c1769e5b7 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Sun, 4 Jun 2023 22:16:32 +0330 Subject: [PATCH 35/79] Add Event Topic For Collection:Transfer Command --- .../blockchain-connector/shared/sdk/constants/eventTopics.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index 28e7d5828..d55cf42fc 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -81,6 +81,7 @@ const { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_CREATED, EVENT_NAME_COLLECTION_ATTRIBUTE_SET, + EVENT_NAME_COLLECTION_TRANSFER, } = require('./names'); @@ -156,6 +157,7 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { [MODULE_NAME_COLLECTION]: { [EVENT_NAME_COLLECTION_CREATED]: ['transactionID', 'senderAddress'], [EVENT_NAME_COLLECTION_ATTRIBUTE_SET]: ['transactionID', 'senderAddress'], + [EVENT_NAME_COLLECTION_TRANSFER]: ['transactionID', 'senderAddress'], }, }; From dc5a302df8bd3b09d5741187b064af96f2196a11 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Sun, 4 Jun 2023 23:46:16 +0330 Subject: [PATCH 36/79] Add Index Transaction for Collection:Transfer Command --- .../shared/sdk/constants/eventTopics.js | 4 +- .../shared/sdk/constants/names.js | 2 + .../collection/transfer.js | 74 +++++++++++++++++++ 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/collection/transfer.js diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index d55cf42fc..25e9d70ad 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -81,7 +81,7 @@ const { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_CREATED, EVENT_NAME_COLLECTION_ATTRIBUTE_SET, - EVENT_NAME_COLLECTION_TRANSFER, + EVENT_NAME_COLLECTION_TRANSFERED, } = require('./names'); @@ -157,7 +157,7 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { [MODULE_NAME_COLLECTION]: { [EVENT_NAME_COLLECTION_CREATED]: ['transactionID', 'senderAddress'], [EVENT_NAME_COLLECTION_ATTRIBUTE_SET]: ['transactionID', 'senderAddress'], - [EVENT_NAME_COLLECTION_TRANSFER]: ['transactionID', 'senderAddress'], + [EVENT_NAME_COLLECTION_TRANSFERED]: ['transactionID', 'senderAddress'], }, }; diff --git a/services/blockchain-connector/shared/sdk/constants/names.js b/services/blockchain-connector/shared/sdk/constants/names.js index d280f3339..ec5025b24 100644 --- a/services/blockchain-connector/shared/sdk/constants/names.js +++ b/services/blockchain-connector/shared/sdk/constants/names.js @@ -94,6 +94,7 @@ const EVENT_NAME_SUBSCRIPTION_PURCHASED = 'subscriptionPurchased'; const MODULE_NAME_COLLECTION = 'collection'; const EVENT_NAME_COLLECTION_CREATED = 'collectionCreated'; const EVENT_NAME_COLLECTION_ATTRIBUTE_SET = 'collectionAttributeSet'; +const EVENT_NAME_COLLECTION_TRANSFERED = 'collectionTransfered'; module.exports = { MODULE_NAME_AUTH, @@ -164,4 +165,5 @@ module.exports = { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_CREATED, EVENT_NAME_COLLECTION_ATTRIBUTE_SET, + EVENT_NAME_COLLECTION_TRANSFERED, }; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/transfer.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/transfer.js new file mode 100644 index 000000000..9ceeff1a0 --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/transfer.js @@ -0,0 +1,74 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); + +const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const accountsTableSchema = require('../../../database/schema/accounts'); +const collectionsTableSchema = require('../../../database/schema/collections'); +const { + MODULE_NAME_COLLECTION, + EVENT_NAME_COLLECTION_CREATED, + EVENT_NAME_COLLECTION_TRANSFERED, +} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); + +const getAccountsTable = () => getTableInstance( + accountsTableSchema.tableName, + accountsTableSchema, + MYSQL_ENDPOINT, +); + +const getCollectionsTable = () => getTableInstance( + collectionsTableSchema.tableName, + collectionsTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'transfer'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const accountsTable = await getAccountsTable(); + const collectionsTable = await getCollectionsTable(); + + const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); + + const account = { + address: senderAddress, + }; + + logger.trace(`Updating account index for the account with address ${account.address}.`); + await accountsTable.upsert(account, dbTrx); + logger.debug(`Updated account index for the account with address ${account.address}.`); + + logger.trace(`Indexing collections with new address ${account.address}.`); + + const { data: eventData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_COLLECTION + && name === EVENT_NAME_COLLECTION_TRANSFERED, + ); + + const collectionsNFT = { + ...eventData, + ...tx.params, + }; + + await collectionsTable.upsert(collectionsNFT, dbTrx); + logger.debug(`Indexed collection with ID ${eventData.collectionsID}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => {}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From c72136c2f42a196baf3a463ed0c76193fb74d42c Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 08:58:23 +0200 Subject: [PATCH 37/79] Fix the command name (setAttributes) --- .../shared/indexer/transactionProcessor/audio/setAttributes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js index 6812d1f15..60095298a 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js @@ -25,7 +25,7 @@ const getFitsTable = () => getTableInstance( ); // Command specific constants -const COMMAND_NAME = 'create'; +const COMMAND_NAME = 'setAttributes'; // eslint-disable-next-line no-unused-vars const applyTransaction = async (blockHeader, tx, events, dbTrx) => { From 100b520d092c1494f5c876161ce6221b57636143 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 09:10:15 +0200 Subject: [PATCH 38/79] Make creatorAddress required --- services/blockchain-indexer/shared/database/schema/audios.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/blockchain-indexer/shared/database/schema/audios.js b/services/blockchain-indexer/shared/database/schema/audios.js index 606aaef10..210293bc7 100644 --- a/services/blockchain-indexer/shared/database/schema/audios.js +++ b/services/blockchain-indexer/shared/database/schema/audios.js @@ -8,7 +8,7 @@ module.exports = { collectionID: { type: 'string' }, audioSignature: { type: 'string', null: true, defaultValue: null }, coverHash: { type: 'string', null: true, defaultValue: null }, - creatorAddress: { type: 'string', null: true, defaultValue: null }, + creatorAddress: { type: 'string' }, }, indexes: { creatorAddress: { type: 'string' }, From 60ef8ec1f13efe82f440054b06b9f900caaba38f Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 09:19:12 +0200 Subject: [PATCH 39/79] Fix typo (fit -> Feat) --- .../shared/dataService/business/audios.js | 14 ++++---- .../database/schema/{fits.js => feats.js} | 2 +- .../transactionProcessor/audio/create.js | 35 +++++++++++-------- .../audio/setAttributes.js | 28 +++++++-------- .../swagger/definitions/audios.json | 4 +-- services/gateway/sources/version3/audios.js | 2 +- .../sources/version3/mappings/audio.js | 2 +- 7 files changed, 46 insertions(+), 41 deletions(-) rename services/blockchain-indexer/shared/database/schema/{fits.js => feats.js} (93%) diff --git a/services/blockchain-indexer/shared/dataService/business/audios.js b/services/blockchain-indexer/shared/dataService/business/audios.js index 841b0f380..803a16642 100644 --- a/services/blockchain-indexer/shared/dataService/business/audios.js +++ b/services/blockchain-indexer/shared/dataService/business/audios.js @@ -6,7 +6,7 @@ const BluebirdPromise = require('bluebird'); const transactionsIndexSchema = require('../../database/schema/audios'); const collectionsIndexSchema = require('../../database/schema/collections'); const ownersIndexSchema = require('../../database/schema/owners'); -const fitsIndexSchema = require('../../database/schema/fits'); +const featsIndexSchema = require('../../database/schema/feats'); const config = require('../../../config'); const MYSQL_ENDPOINT = config.endpoints.mysql; @@ -29,9 +29,9 @@ const getOwnersIndex = () => getTableInstance( MYSQL_ENDPOINT, ); -const getFitsIndex = () => getTableInstance( - fitsIndexSchema.tableName, - fitsIndexSchema, +const getFeatsIndex = () => getTableInstance( + featsIndexSchema.tableName, + featsIndexSchema, MYSQL_ENDPOINT, ); @@ -39,7 +39,7 @@ const getAudios = async (params = {}) => { const audiosTable = await getAudiosIndex(); const collectionsTable = await getCollectionsIndex(); const ownersTable = await getOwnersIndex(); - const fitsTable = await getFitsIndex(); + const featsTable = await getFeatsIndex(); const total = await audiosTable.count(params); const audioData = await audiosTable.find( @@ -60,7 +60,7 @@ const getAudios = async (params = {}) => { ['address', 'share', 'income'], ); - const fitData = await fitsTable.find( + const featData = await featsTable.find( { audioID: audio.audioID }, ['address', 'role'], ); @@ -69,7 +69,7 @@ const getAudios = async (params = {}) => { ...audio, collection: collectionData.length ? collectionData[0] : {}, owners: ownersData, - fit: fitData, + feat: featData, }; }, { concurrency: audioData.length }, diff --git a/services/blockchain-indexer/shared/database/schema/fits.js b/services/blockchain-indexer/shared/database/schema/feats.js similarity index 93% rename from services/blockchain-indexer/shared/database/schema/fits.js rename to services/blockchain-indexer/shared/database/schema/feats.js index 0503930af..aabaff8d9 100644 --- a/services/blockchain-indexer/shared/database/schema/fits.js +++ b/services/blockchain-indexer/shared/database/schema/feats.js @@ -1,5 +1,5 @@ module.exports = { - tableName: 'fits', + tableName: 'feats', primaryKey: ['address', 'audioID'], schema: { address: { type: 'string', null: true, defaultValue: null }, diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js index a7a308126..42735cf5f 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js @@ -14,7 +14,7 @@ const MYSQL_ENDPOINT = config.endpoints.mysql; const accountsTableSchema = require('../../../database/schema/accounts'); const audiosTableSchema = require('../../../database/schema/audios'); const ownersTableSchema = require('../../../database/schema/owners'); -const fitsTableSchema = require('../../../database/schema/fits'); +const featsTableSchema = require('../../../database/schema/feats'); const { MODULE_NAME_AUDIO, EVENT_NAME_AUDIO_CREATED, @@ -38,9 +38,9 @@ const getOwnersTable = () => getTableInstance( MYSQL_ENDPOINT, ); -const getFitsTable = () => getTableInstance( - fitsTableSchema.tableName, - fitsTableSchema, +const getFeatsTable = () => getTableInstance( + featsTableSchema.tableName, + featsTableSchema, MYSQL_ENDPOINT, ); @@ -52,7 +52,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const accountsTable = await getAccountsTable(); const audiosTable = await getAudiosTable(); const ownersTable = await getOwnersTable(); - const fitsTable = await getFitsTable(); + const featsTable = await getFeatsTable(); const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); @@ -87,21 +87,21 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { { concurrency: tx.params.owners.length }, ); - // Insert fits + // Insert feats await BluebirdPromise.map( - tx.params.fit, - async fit => { - const fitInfo = { - address: fit, - role: 'co-artist', // TODO: get role from tx.params.fit + tx.params.feat, + async feat => { + const featInfo = { + address: feat, + role: 'co-artist', // TODO: get role from tx.params.feat audioID: eventData.audioID, }; - logger.trace(`Updating fits index for the account with address ${fit}.`); - await fitsTable.upsert(fitInfo, dbTrx); - logger.debug(`Updated fits index for the account with address ${fit}.`); + logger.trace(`Updating feats index for the account with address ${feat}.`); + await featsTable.upsert(featInfo, dbTrx); + logger.debug(`Updated feats index for the account with address ${feat}.`); return true; }, - { concurrency: tx.params.fit.length }, + { concurrency: tx.params.feat.length }, ); logger.trace(`Updating owners index for the audio with audioID ${account.address}.`); @@ -123,6 +123,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const revertTransaction = async (blockHeader, tx, events, dbTrx) => { const audiosTable = await getAudiosTable(); const ownersTable = await getOwnersTable(); + const featsTable = await getFeatsTable(); const { data: eventData = {} } = events.find( ({ module, name }) => module === MODULE_NAME_AUDIO @@ -133,6 +134,10 @@ const revertTransaction = async (blockHeader, tx, events, dbTrx) => { await ownersTable.delete({ audioID: eventData.audioID }, dbTrx); logger.trace(`Deleted owners corresponding the audio ID ${eventData.audioID}.`); + logger.trace(`Deleting feats corresponding the audio ID ${eventData.audioID}.`); + await featsTable.delete({ audioID: eventData.audioID }, dbTrx); + logger.trace(`Deleted feats corresponding the audio ID ${eventData.audioID}.`); + logger.trace(`Removing audio entry for ID ${eventData.audioID}.`); await audiosTable.deleteByPrimaryKey(eventData.audioID, dbTrx); logger.debug(`Removed audio entry for ID ${eventData.audioID}.`); diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js index 60095298a..7d0199363 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js @@ -10,7 +10,7 @@ const logger = Logger(); const MYSQL_ENDPOINT = config.endpoints.mysql; const audiosTableSchema = require('../../../database/schema/audios'); -const fitsTableSchema = require('../../../database/schema/fits'); +const featsTableSchema = require('../../../database/schema/feats'); const getAudiosTable = () => getTableInstance( audiosTableSchema.tableName, @@ -19,8 +19,8 @@ const getAudiosTable = () => getTableInstance( ); const getFitsTable = () => getTableInstance( - fitsTableSchema.tableName, - fitsTableSchema, + featsTableSchema.tableName, + featsTableSchema, MYSQL_ENDPOINT, ); @@ -30,7 +30,7 @@ const COMMAND_NAME = 'setAttributes'; // eslint-disable-next-line no-unused-vars const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const audiosTable = await getAudiosTable(); - const fitsTable = await getFitsTable(); + const featsTable = await getFitsTable(); const audio = await audiosTable.find( { audioID: tx.params.audioID }, @@ -38,21 +38,21 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { dbTrx, ); - // Insert fits + // Insert feats await BluebirdPromise.map( - tx.params.fit, - async fit => { - const fitInfo = { - address: fit, - role: 'co-artist', // TODO: get role from tx.params.fit + tx.params.feat, + async feat => { + const featInfo = { + address: feat, + role: 'co-artist', // TODO: get role from tx.params.feat audioID: tx.params.audioID, }; - logger.trace(`Updating fits index for the account with address ${fit}.`); - await fitsTable.upsert(fitInfo, dbTrx); - logger.debug(`Updated fits index for the account with address ${fit}.`); + logger.trace(`Updating feats index for the account with address ${feat}.`); + await featsTable.upsert(featInfo, dbTrx); + logger.debug(`Updated feats index for the account with address ${feat}.`); return true; }, - { concurrency: tx.params.fit.length }, + { concurrency: tx.params.feat.length }, ); logger.trace(`Updating audio with ID ${tx.params.audioID}.`); diff --git a/services/gateway/apis/http-version3/swagger/definitions/audios.json b/services/gateway/apis/http-version3/swagger/definitions/audios.json index ab64ba989..5b5e94998 100644 --- a/services/gateway/apis/http-version3/swagger/definitions/audios.json +++ b/services/gateway/apis/http-version3/swagger/definitions/audios.json @@ -7,7 +7,7 @@ "releaseYear", "collectionID", "creatorAddress", - "fit" + "feat" ], "properties": { "audioID": { @@ -56,7 +56,7 @@ } } }, - "fit": { + "feat": { "type": "object", "required": [ "address", diff --git a/services/gateway/sources/version3/audios.js b/services/gateway/sources/version3/audios.js index 834c3a8cd..b0757a718 100644 --- a/services/gateway/sources/version3/audios.js +++ b/services/gateway/sources/version3/audios.js @@ -19,7 +19,7 @@ module.exports = { shares: '=,number', income: '=,number', }], - fit: ['fit', { + feat: ['feat', { address: '=,string', name: '=,string', role: '=,string', diff --git a/services/gateway/sources/version3/mappings/audio.js b/services/gateway/sources/version3/mappings/audio.js index 10b44cabf..a70f0c474 100644 --- a/services/gateway/sources/version3/mappings/audio.js +++ b/services/gateway/sources/version3/mappings/audio.js @@ -14,7 +14,7 @@ module.exports = { shares: '=,number', income: '=,number', }], - fit: ['fit', { + feat: ['feat', { address: '=,string', role: '=,string', }], From de7b50d869570ed39785b62628f18551cd7e03a9 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 10:47:46 +0200 Subject: [PATCH 40/79] Remove nonce from owners --- services/blockchain-indexer/shared/database/schema/owners.js | 3 +-- .../shared/indexer/transactionProcessor/audio/create.js | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/services/blockchain-indexer/shared/database/schema/owners.js b/services/blockchain-indexer/shared/database/schema/owners.js index ce2145cbd..a26efce14 100644 --- a/services/blockchain-indexer/shared/database/schema/owners.js +++ b/services/blockchain-indexer/shared/database/schema/owners.js @@ -4,8 +4,7 @@ module.exports = { schema: { address: { type: 'string', null: true, defaultValue: null }, audioID: { type: 'string' }, - nonce: { type: 'bigInteger', min: 0 }, - share: { type: 'integer', min: 1, max: 100 }, + shares: { type: 'integer', min: 1, max: 100 }, income: { type: 'bigInteger', null: true, defaultValue: 0 }, }, indexes: { diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js index 42735cf5f..a47119c02 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js @@ -76,7 +76,6 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const memberInfo = { ...owner, audioID: eventData.audioID, - nonce: tx.nonce, shares: 0, }; logger.trace(`Updating owner index for the account with address ${owner.address}.`); From e08df51be8cb9e44cdabf6985ccfa0e0d159f7d4 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 10:48:10 +0200 Subject: [PATCH 41/79] Add support for audio:transfer transactions --- .../transactionProcessor/audio/transfer.js | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/audio/transfer.js diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/transfer.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/transfer.js new file mode 100644 index 000000000..424752870 --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/transfer.js @@ -0,0 +1,69 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); + +const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const ownersTableSchema = require('../../../database/schema/owners'); + +const getOwnersTable = () => getTableInstance( + ownersTableSchema.tableName, + ownersTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'transfer'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const ownersTable = await getOwnersTable(); + + const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); + const { + address: recipientAddress, + audioID, + shares: transferredShares, + } = tx.params.address; + + const sender = await ownersTable.find({ audioID, address: senderAddress }, dbTrx); + let recipient = await ownersTable.find({ audioID, address: recipientAddress }, dbTrx); + + // Transfer the shares + logger.trace(`Updating owner index for the account with address ${senderAddress}.`); + sender.shares -= transferredShares; + if (sender.shares === 0) { + await ownersTable.delete({ audioID: tx.params.audioID, address: senderAddress }, dbTrx); + } else { + await ownersTable.upsert(sender, dbTrx); + } + logger.debug(`Updated owner index for the account with address ${senderAddress}.`); + + logger.trace(`Updating owner index for the account with address ${recipientAddress}.`); + if (recipient) { + recipient.shares += transferredShares; + } else { + recipient = { + audioID, + address: recipientAddress, + shares: transferredShares, + }; + } + await ownersTable.upsert(recipient, dbTrx); + logger.debug(`Updated owner index for the account with address ${recipientAddress}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => {}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From b0dab841a63c23a6e44c1ba80248c1c9f9ac0a7a Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Mon, 5 Jun 2023 15:54:42 +0330 Subject: [PATCH 42/79] Update Collection:Transfer Indexing Feature --- .../collection/transfer.js | 45 ++++--------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/transfer.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/transfer.js index 9ceeff1a0..70e5b533c 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/transfer.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/transfer.js @@ -3,26 +3,12 @@ const { MySQL: { getTableInstance }, } = require('lisk-service-framework'); -const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); - const config = require('../../../../config'); const logger = Logger(); const MYSQL_ENDPOINT = config.endpoints.mysql; -const accountsTableSchema = require('../../../database/schema/accounts'); const collectionsTableSchema = require('../../../database/schema/collections'); -const { - MODULE_NAME_COLLECTION, - EVENT_NAME_COLLECTION_CREATED, - EVENT_NAME_COLLECTION_TRANSFERED, -} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); - -const getAccountsTable = () => getTableInstance( - accountsTableSchema.tableName, - accountsTableSchema, - MYSQL_ENDPOINT, -); const getCollectionsTable = () => getTableInstance( collectionsTableSchema.tableName, @@ -35,33 +21,18 @@ const COMMAND_NAME = 'transfer'; // eslint-disable-next-line no-unused-vars const applyTransaction = async (blockHeader, tx, events, dbTrx) => { - const accountsTable = await getAccountsTable(); const collectionsTable = await getCollectionsTable(); - - const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); - - const account = { - address: senderAddress, - }; - - logger.trace(`Updating account index for the account with address ${account.address}.`); - await accountsTable.upsert(account, dbTrx); - logger.debug(`Updated account index for the account with address ${account.address}.`); - - logger.trace(`Indexing collections with new address ${account.address}.`); - - const { data: eventData = {} } = events.find( - ({ module, name }) => module === MODULE_NAME_COLLECTION - && name === EVENT_NAME_COLLECTION_TRANSFERED, + const [collectionNFT] = await collectionsTable.find( + { collectionID: tx.params.collectionID }, + ['collectionID', 'name', 'collectionType', 'releaseYear'], + dbTrx, ); - const collectionsNFT = { - ...eventData, - ...tx.params, - }; + collectionNFT.creatorAddress = tx.params.address; + logger.trace(`Indexing collections with new address ${collectionNFT.creatorAddress}.`); - await collectionsTable.upsert(collectionsNFT, dbTrx); - logger.debug(`Indexed collection with ID ${eventData.collectionsID}.`); + await collectionsTable.upsert(collectionNFT, dbTrx); + logger.debug(`Indexed collection with ID ${collectionNFT.collectionsID}.`); }; // eslint-disable-next-line no-unused-vars From 48f8c6b30c495b043e48a487204783ef75af5249 Mon Sep 17 00:00:00 2001 From: curvesy Date: Mon, 5 Jun 2023 18:54:46 +0330 Subject: [PATCH 43/79] Created destroy file for indexing collection destroy transactions --- .../collection/destroy.js | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js new file mode 100644 index 000000000..ab3f007d1 --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js @@ -0,0 +1,42 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const collectionsTableSchema = require('../../../database/schema/collections'); + +const getCollectionsTable = () => getTableInstance( + collectionsTableSchema.tableName, + collectionsTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'destroy'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const collectionsTable = await getCollectionsTable(); + const [collectionNFT] = await collectionsTable.find( + { collectionID: tx.params.collectionID }, + ['collectionID'], + dbTrx, + ); + + await collectionsTable.delete(collectionNFT, dbTrx); + logger.debug(`Deleted collection with ID ${collectionNFT.collectionsID}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => {}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From ed87502b130da307f091d828319c21c5dab01920 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 22:30:32 +0200 Subject: [PATCH 44/79] Fix audio db schema according to the blockchain definitions --- .../blockchain-indexer/shared/database/schema/audios.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/blockchain-indexer/shared/database/schema/audios.js b/services/blockchain-indexer/shared/database/schema/audios.js index 210293bc7..0010a770a 100644 --- a/services/blockchain-indexer/shared/database/schema/audios.js +++ b/services/blockchain-indexer/shared/database/schema/audios.js @@ -3,11 +3,11 @@ module.exports = { primaryKey: 'audioID', schema: { audioID: { type: 'string' }, - name: { type: 'string', null: true, defaultValue: null }, - releaseYear: { type: 'string', null: true, defaultValue: null }, + name: { type: 'string' }, + releaseYear: { type: 'string' }, collectionID: { type: 'string' }, - audioSignature: { type: 'string', null: true, defaultValue: null }, - coverHash: { type: 'string', null: true, defaultValue: null }, + audioSignature: { type: 'string' }, + audioHash: { type: 'string' }, creatorAddress: { type: 'string' }, }, indexes: { From 74865fbb6692293712881bbb226be2955e032d0f Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 22:30:40 +0200 Subject: [PATCH 45/79] Fix owners db schema according to the blockchain definitions --- .../blockchain-indexer/shared/database/schema/owners.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/blockchain-indexer/shared/database/schema/owners.js b/services/blockchain-indexer/shared/database/schema/owners.js index a26efce14..304748ba3 100644 --- a/services/blockchain-indexer/shared/database/schema/owners.js +++ b/services/blockchain-indexer/shared/database/schema/owners.js @@ -1,11 +1,11 @@ module.exports = { tableName: 'owners', - primaryKey: ['address', 'audioID', 'nonce'], + primaryKey: ['address', 'audioID'], schema: { - address: { type: 'string', null: true, defaultValue: null }, + address: { type: 'string' }, audioID: { type: 'string' }, - shares: { type: 'integer', min: 1, max: 100 }, - income: { type: 'bigInteger', null: true, defaultValue: 0 }, + shares: { type: 'integer' }, + income: { type: 'bigInteger', defaultValue: 0 }, }, indexes: { creatorAddress: { type: 'string' }, From 47546cfa68d383260a71e93851b47af97940fc9b Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 22:31:17 +0200 Subject: [PATCH 46/79] Fix the use of owners properties in data retrieval --- .../blockchain-indexer/shared/dataService/business/audios.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/blockchain-indexer/shared/dataService/business/audios.js b/services/blockchain-indexer/shared/dataService/business/audios.js index 803a16642..d1f6b14bc 100644 --- a/services/blockchain-indexer/shared/dataService/business/audios.js +++ b/services/blockchain-indexer/shared/dataService/business/audios.js @@ -57,7 +57,7 @@ const getAudios = async (params = {}) => { const ownersData = await ownersTable.find( { audioID: audio.audioID }, - ['address', 'share', 'income'], + ['address', 'shares', 'income'], ); const featData = await featsTable.find( From bcfc7753df4e4d426d980160334f624c723c38a9 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 22:32:27 +0200 Subject: [PATCH 47/79] Improve audio:create to not process failed txs, and store owners correctly --- .../transactionProcessor/audio/create.js | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js index a47119c02..375af16f2 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js @@ -18,6 +18,7 @@ const featsTableSchema = require('../../../database/schema/feats'); const { MODULE_NAME_AUDIO, EVENT_NAME_AUDIO_CREATED, + EVENT_NAME_COMMAND_EXECUTION_RESULT, } = require('../../../../../blockchain-connector/shared/sdk/constants/names'); const getAccountsTable = () => getTableInstance( @@ -49,13 +50,29 @@ const COMMAND_NAME = 'create'; // eslint-disable-next-line no-unused-vars const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + // Do not process failed transactions + const { data: commandExecutedData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_AUDIO + && name === EVENT_NAME_COMMAND_EXECUTION_RESULT, + ); + if (!commandExecutedData.success) { + return false; + } + const accountsTable = await getAccountsTable(); const audiosTable = await getAudiosTable(); const ownersTable = await getOwnersTable(); const featsTable = await getFeatsTable(); + // Use event data to get audioID + const { data: audioCreatedData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_AUDIO + && name === EVENT_NAME_AUDIO_CREATED, + ); + const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); + // Store a record of the sender account const account = { address: senderAddress, }; @@ -64,19 +81,13 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { await accountsTable.upsert(account, dbTrx); logger.debug(`Updated account index for the account with address ${account.address}.`); - const { data: eventData = {} } = events.find( - ({ module, name }) => module === MODULE_NAME_AUDIO - && name === EVENT_NAME_AUDIO_CREATED, - ); - - // Insert owners + // Store owners await BluebirdPromise.map( tx.params.owners, async owner => { const memberInfo = { ...owner, - audioID: eventData.audioID, - shares: 0, + audioID: audioCreatedData.audioID, }; logger.trace(`Updating owner index for the account with address ${owner.address}.`); await ownersTable.upsert(memberInfo, dbTrx); @@ -86,14 +97,14 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { { concurrency: tx.params.owners.length }, ); - // Insert feats + // Store feats await BluebirdPromise.map( tx.params.feat, async feat => { const featInfo = { address: feat, - role: 'co-artist', // TODO: get role from tx.params.feat - audioID: eventData.audioID, + role: 'co-artist', + audioID: audioCreatedData.audioID, }; logger.trace(`Updating feats index for the account with address ${feat}.`); await featsTable.upsert(featInfo, dbTrx); @@ -109,13 +120,15 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { logger.trace(`Indexing audios with address ${account.address}.`); + // And finally, store the audio const audiosNFT = { - ...eventData, + ...audioCreatedData, ...tx.params, }; await audiosTable.upsert(audiosNFT, dbTrx); - logger.debug(`Indexed audio with ID ${eventData.audiosID}.`); + logger.debug(`Indexed audio with ID ${audioCreatedData.audiosID}.`); + return true; }; // eslint-disable-next-line no-unused-vars @@ -124,22 +137,22 @@ const revertTransaction = async (blockHeader, tx, events, dbTrx) => { const ownersTable = await getOwnersTable(); const featsTable = await getFeatsTable(); - const { data: eventData = {} } = events.find( + const { data: audioCreatedData = {} } = events.find( ({ module, name }) => module === MODULE_NAME_AUDIO && name === EVENT_NAME_AUDIO_CREATED, ); - logger.trace(`Deleting owners corresponding the audio ID ${eventData.audioID}.`); - await ownersTable.delete({ audioID: eventData.audioID }, dbTrx); - logger.trace(`Deleted owners corresponding the audio ID ${eventData.audioID}.`); + logger.trace(`Deleting owners corresponding the audio ID ${audioCreatedData.audioID}.`); + await ownersTable.delete({ audioID: audioCreatedData.audioID }, dbTrx); + logger.trace(`Deleted owners corresponding the audio ID ${audioCreatedData.audioID}.`); - logger.trace(`Deleting feats corresponding the audio ID ${eventData.audioID}.`); - await featsTable.delete({ audioID: eventData.audioID }, dbTrx); - logger.trace(`Deleted feats corresponding the audio ID ${eventData.audioID}.`); + logger.trace(`Deleting feats corresponding the audio ID ${audioCreatedData.audioID}.`); + await featsTable.delete({ audioID: audioCreatedData.audioID }, dbTrx); + logger.trace(`Deleted feats corresponding the audio ID ${audioCreatedData.audioID}.`); - logger.trace(`Removing audio entry for ID ${eventData.audioID}.`); - await audiosTable.deleteByPrimaryKey(eventData.audioID, dbTrx); - logger.debug(`Removed audio entry for ID ${eventData.audioID}.`); + logger.trace(`Removing audio entry for ID ${audioCreatedData.audioID}.`); + await audiosTable.deleteByPrimaryKey(audioCreatedData.audioID, dbTrx); + logger.debug(`Removed audio entry for ID ${audioCreatedData.audioID}.`); }; module.exports = { From e94cbce654eba82e279505cb06bd45e979330216 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 22:32:59 +0200 Subject: [PATCH 48/79] Improve audio:setAttribute to not process failed txs, and account for removed owners --- .../audio/setAttributes.js | 66 +++++++++++++++---- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js index 7d0199363..3c97464e7 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/setAttributes.js @@ -12,6 +12,11 @@ const MYSQL_ENDPOINT = config.endpoints.mysql; const audiosTableSchema = require('../../../database/schema/audios'); const featsTableSchema = require('../../../database/schema/feats'); +const { + MODULE_NAME_AUDIO, + EVENT_NAME_COMMAND_EXECUTION_RESULT, +} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); + const getAudiosTable = () => getTableInstance( audiosTableSchema.tableName, audiosTableSchema, @@ -29,40 +34,79 @@ const COMMAND_NAME = 'setAttributes'; // eslint-disable-next-line no-unused-vars const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + // Do not process failed transactions + const { data: commandExecutedData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_AUDIO + && name === EVENT_NAME_COMMAND_EXECUTION_RESULT, + ); + if (!commandExecutedData.success) { + return false; + } + const audiosTable = await getAudiosTable(); const featsTable = await getFitsTable(); - const audio = await audiosTable.find( - { audioID: tx.params.audioID }, + const { audioID } = tx.params; + + // Find the audio to process + const [audio] = await audiosTable.find( + { audioID }, ['name', 'releaseYear', 'collectionID', 'creatorAddress'], dbTrx, ); + const oldFeats = await featsTable.find( + { audioID }, + ['address'], + dbTrx, + ); + const oldFeatsAddresses = oldFeats.map(({ address }) => address); + + // Define removed and added feats + const removedFeats = oldFeatsAddresses.filter( + address => !tx.params.feat.includes(address), + ); + const addedFeats = tx.params.feat.filter( + address => !oldFeatsAddresses.includes(address), + ); - // Insert feats + // Store new feats await BluebirdPromise.map( - tx.params.feat, - async feat => { + addedFeats, + async address => { const featInfo = { - address: feat, + address, role: 'co-artist', // TODO: get role from tx.params.feat - audioID: tx.params.audioID, + audioID, }; - logger.trace(`Updating feats index for the account with address ${feat}.`); + logger.trace(`Updating feats index for the account with address ${address}.`); await featsTable.upsert(featInfo, dbTrx); - logger.debug(`Updated feats index for the account with address ${feat}.`); + logger.debug(`Updated feats index for the account with address ${address}.`); + return true; + }, + { concurrency: tx.params.feat.length }, + ); + + // Remove old feats + await BluebirdPromise.map( + removedFeats, + async address => { + logger.trace(`Updating feats index for the account with address ${address}.`); + await featsTable.delete({ address, audioID }, dbTrx); + logger.debug(`Updated feats index for the account with address ${address}.`); return true; }, { concurrency: tx.params.feat.length }, ); - logger.trace(`Updating audio with ID ${tx.params.audioID}.`); + logger.trace(`Updating audio with ID ${audioID}.`); const audiosNFT = { ...audio, ...tx.params, }; await audiosTable.upsert(audiosNFT, dbTrx); - logger.debug(`Updated audio with ID ${tx.params.audioID}.`); + logger.debug(`Updated audio with ID ${audioID}.`); + return true; }; // eslint-disable-next-line no-unused-vars From 9df2ecb04191ca20e7233ffed2471c7a5a9b2eb0 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 22:33:29 +0200 Subject: [PATCH 49/79] Improve audio:transfer to not process failed txs --- .../transactionProcessor/audio/transfer.js | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/transfer.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/transfer.js index 424752870..53104eb2c 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/transfer.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/transfer.js @@ -12,6 +12,11 @@ const logger = Logger(); const MYSQL_ENDPOINT = config.endpoints.mysql; const ownersTableSchema = require('../../../database/schema/owners'); +const { + MODULE_NAME_AUDIO, + EVENT_NAME_COMMAND_EXECUTION_RESULT, +} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); + const getOwnersTable = () => getTableInstance( ownersTableSchema.tableName, ownersTableSchema, @@ -23,17 +28,25 @@ const COMMAND_NAME = 'transfer'; // eslint-disable-next-line no-unused-vars const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const { data: commandExecutedData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_AUDIO + && name === EVENT_NAME_COMMAND_EXECUTION_RESULT, + ); + if (!commandExecutedData.success) { + return false; + } + const ownersTable = await getOwnersTable(); const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); const { - address: recipientAddress, audioID, + address: recipientAddress, shares: transferredShares, - } = tx.params.address; + } = tx.params; - const sender = await ownersTable.find({ audioID, address: senderAddress }, dbTrx); - let recipient = await ownersTable.find({ audioID, address: recipientAddress }, dbTrx); + const [sender] = await ownersTable.find({ audioID, address: senderAddress }, ['address', 'audioID', 'shares', 'income'], dbTrx); + let [recipient] = await ownersTable.find({ audioID, address: recipientAddress }, ['address', 'audioID', 'shares', 'income'], dbTrx); // Transfer the shares logger.trace(`Updating owner index for the account with address ${senderAddress}.`); @@ -43,6 +56,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { } else { await ownersTable.upsert(sender, dbTrx); } + logger.debug(`Updated owner index for the account with address ${senderAddress}.`); logger.trace(`Updating owner index for the account with address ${recipientAddress}.`); @@ -57,6 +71,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { } await ownersTable.upsert(recipient, dbTrx); logger.debug(`Updated owner index for the account with address ${recipientAddress}.`); + return true; }; // eslint-disable-next-line no-unused-vars From bd23f2ba4aee9dd0a94d82d8ebd670a10c267900 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Mon, 5 Jun 2023 22:51:24 +0200 Subject: [PATCH 50/79] Add support for the audio:destroy command --- .../transactionProcessor/audio/destroy.js | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/audio/destroy.js diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/destroy.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/destroy.js new file mode 100644 index 000000000..824c1a9fc --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/destroy.js @@ -0,0 +1,79 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const audiosTableSchema = require('../../../database/schema/audios'); +const ownersTableSchema = require('../../../database/schema/owners'); +const featsTableSchema = require('../../../database/schema/feats'); + +const { + MODULE_NAME_AUDIO, + EVENT_NAME_COMMAND_EXECUTION_RESULT, +} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); + +const getAudiosTable = () => getTableInstance( + audiosTableSchema.tableName, + audiosTableSchema, + MYSQL_ENDPOINT, +); + +const getOwnersTable = () => getTableInstance( + ownersTableSchema.tableName, + ownersTableSchema, + MYSQL_ENDPOINT, +); + +const getFeatsTable = () => getTableInstance( + featsTableSchema.tableName, + featsTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'destroy'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const { data: commandExecutedData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_AUDIO + && name === EVENT_NAME_COMMAND_EXECUTION_RESULT, + ); + if (!commandExecutedData.success) { + return false; + } + + const { audioID } = tx.params; + + const audiosTable = await getAudiosTable(); + const ownersTable = await getOwnersTable(); + const featsTable = await getFeatsTable(); + + logger.trace(`Removing audio index for the audio with ID ${audioID}.`); + await audiosTable.delete({ audioID }, dbTrx); + logger.trace(`Removed audio index for the audio with ID ${audioID}.`); + + logger.trace(`Removing owners index for the audio with ID ${audioID}.`); + await ownersTable.delete({ audioID }, dbTrx); + logger.trace(`Removed owners index for the audio with ID ${audioID}.`); + + logger.trace(`Removing feats index for the audio with ID ${audioID}.`); + await featsTable.delete({ audioID }, dbTrx); + logger.trace(`Removed feats index for the audio with ID ${audioID}.`); + + return true; +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => {}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From b74d40e13899eda21165ccd04ef7c7e35a8882e8 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Tue, 6 Jun 2023 16:01:00 +0330 Subject: [PATCH 51/79] Add trace level logging for collection destroy command --- .../shared/indexer/transactionProcessor/collection/destroy.js | 1 + 1 file changed, 1 insertion(+) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js index ab3f007d1..6416e0254 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js @@ -28,6 +28,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { dbTrx, ); + logger.trace(`Deleting collection with ID ${collectionNFT.collectionID}.`); await collectionsTable.delete(collectionNFT, dbTrx); logger.debug(`Deleted collection with ID ${collectionNFT.collectionsID}.`); }; From 1ae1796bd9db17230f062f7566b2d72c4d81b341 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Tue, 6 Jun 2023 22:29:34 +0330 Subject: [PATCH 52/79] Add profile event constants to connector --- .../shared/sdk/constants/eventTopics.js | 6 ++++++ .../blockchain-connector/shared/sdk/constants/names.js | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index 4bd0aa4a9..54fd3c810 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -85,6 +85,9 @@ const { MODULE_NAME_AUDIO, EVENT_NAME_AUDIO_CREATED, + + MODULE_NAME_PROFILE, + EVENT_NAME_PROFILE_CREATED, } = require('./names'); const COMMAND_EXECUTION_RESULT_TOPICS = ['transactionID']; @@ -164,6 +167,9 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { [MODULE_NAME_AUDIO]: { [EVENT_NAME_AUDIO_CREATED]: ['transactionID', 'senderAddress'], }, + [MODULE_NAME_PROFILE]: { + [EVENT_NAME_PROFILE_CREATED]: ['transactionID', 'senderAddress'], + }, }; module.exports = { diff --git a/services/blockchain-connector/shared/sdk/constants/names.js b/services/blockchain-connector/shared/sdk/constants/names.js index cfe3934f4..f328047d2 100644 --- a/services/blockchain-connector/shared/sdk/constants/names.js +++ b/services/blockchain-connector/shared/sdk/constants/names.js @@ -100,6 +100,10 @@ const EVENT_NAME_COLLECTION_TRANSFERED = 'collectionTransfered'; const MODULE_NAME_AUDIO = 'audio'; const EVENT_NAME_AUDIO_CREATED = 'audioCreated'; +// Audios +const MODULE_NAME_PROFILE = 'profile'; +const EVENT_NAME_PROFILE_CREATED = 'profileCreated'; + module.exports = { MODULE_NAME_AUTH, EVENT_NAME_MULTISIGNATURE_REGISTERED, @@ -173,4 +177,7 @@ module.exports = { MODULE_NAME_AUDIO, EVENT_NAME_AUDIO_CREATED, + + MODULE_NAME_PROFILE, + EVENT_NAME_PROFILE_CREATED, }; From 7b45d0a7ff95575a28df6a518375710c5e91e59e Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Tue, 6 Jun 2023 22:36:15 +0330 Subject: [PATCH 53/79] Add profile data service logic to indexer --- .../dataService/controllers/profiles.js | 31 ++++++++++++++++ .../methods/dataService/modules/profile.js | 16 +++++++++ .../shared/dataService/business/index.js | 6 ++++ .../shared/dataService/business/profiles.js | 36 +++++++++++++++++++ .../shared/dataService/index.js | 4 +++ .../shared/dataService/profiles.js | 22 ++++++++++++ 6 files changed, 115 insertions(+) create mode 100644 services/blockchain-indexer/methods/dataService/controllers/profiles.js create mode 100644 services/blockchain-indexer/methods/dataService/modules/profile.js create mode 100644 services/blockchain-indexer/shared/dataService/business/profiles.js create mode 100644 services/blockchain-indexer/shared/dataService/profiles.js diff --git a/services/blockchain-indexer/methods/dataService/controllers/profiles.js b/services/blockchain-indexer/methods/dataService/controllers/profiles.js new file mode 100644 index 000000000..512ea767a --- /dev/null +++ b/services/blockchain-indexer/methods/dataService/controllers/profiles.js @@ -0,0 +1,31 @@ +const { + HTTP: { StatusCodes: { BAD_REQUEST } }, + Exceptions: { ValidationException, InvalidParamsException }, +} = require('lisk-service-framework'); + +const dataService = require('../../../shared/dataService'); + +const getProfiles = async params => { + const profiles = { + data: [], + meta: {}, + }; + + try { + const response = await dataService.getProfiles(params); + if (response.data) profiles.data = response.data; + if (response.meta) profiles.meta = response.meta; + + return profiles; + } catch (err) { + let status; + if (err instanceof InvalidParamsException) status = 'INVALID_PARAMS'; + if (err instanceof ValidationException) status = BAD_REQUEST; + if (status) return { status, data: { error: err.message } }; + throw err; + } +}; + +module.exports = { + getProfiles, +}; diff --git a/services/blockchain-indexer/methods/dataService/modules/profile.js b/services/blockchain-indexer/methods/dataService/modules/profile.js new file mode 100644 index 000000000..b09cf5875 --- /dev/null +++ b/services/blockchain-indexer/methods/dataService/modules/profile.js @@ -0,0 +1,16 @@ +const { + getProfiles, +} = require('../controllers/profiles'); + +module.exports = [ + { + name: 'profiles', + controller: getProfiles, + params: { + creatorAddress: { optional: true, type: 'string' }, + profileID: { optional: true, type: 'string' }, + limit: { optional: true, type: 'number' }, + offset: { optional: true, type: 'number' }, + }, + }, +]; diff --git a/services/blockchain-indexer/shared/dataService/business/index.js b/services/blockchain-indexer/shared/dataService/business/index.js index ff21d7467..55f285076 100644 --- a/services/blockchain-indexer/shared/dataService/business/index.js +++ b/services/blockchain-indexer/shared/dataService/business/index.js @@ -94,9 +94,12 @@ const { getNetworkDisconnectedPeers, getNetworkPeersStatistics, } = require('./network'); + +// Muzikie Dedicated Modules const { getSubscriptions } = require('./subscriptions'); const { getCollections } = require('./collections'); const { getAudios } = require('./audios'); +const { getProfiles } = require('./profiles'); module.exports = { // Generators @@ -187,4 +190,7 @@ module.exports = { // audios getAudios, + + // profiles + getProfiles, }; diff --git a/services/blockchain-indexer/shared/dataService/business/profiles.js b/services/blockchain-indexer/shared/dataService/business/profiles.js new file mode 100644 index 000000000..303b9a7db --- /dev/null +++ b/services/blockchain-indexer/shared/dataService/business/profiles.js @@ -0,0 +1,36 @@ +const { + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); +const transactionsIndexSchema = require('../../database/schema/profiles'); +const config = require('../../../config'); + +const MYSQL_ENDPOINT = config.endpoints.mysql; + +const getProfilesIndex = () => getTableInstance( + transactionsIndexSchema.tableName, + transactionsIndexSchema, + MYSQL_ENDPOINT, +); + +const getProfiles = async (params = {}) => { + const profilesTable = await getProfilesIndex(); + const total = await profilesTable.count(params); + const resultSet = await profilesTable.find( + { ...params, limit: params.limit || 10 }, + ['profileID', 'name', 'nickName', 'description', 'socialAccounts', 'creatorAddress'], + ); + + const result = { + data: resultSet, + meta: { + count: resultSet.length, + offset: parseInt(params.offset, 10) || 0, + total, + }, + }; + return result; +}; + +module.exports = { + getProfiles, +}; diff --git a/services/blockchain-indexer/shared/dataService/index.js b/services/blockchain-indexer/shared/dataService/index.js index 38a2926c9..d6c93a838 100644 --- a/services/blockchain-indexer/shared/dataService/index.js +++ b/services/blockchain-indexer/shared/dataService/index.js @@ -96,6 +96,7 @@ const { getGenerators } = require('./generators'); const { getSubscriptions } = require('./subscriptions'); const { getCollections } = require('./collections'); const { getAudios } = require('./audios'); +const { getProfiles } = require('./profiles'); module.exports = { // Blocks @@ -192,4 +193,7 @@ module.exports = { // Audios getAudios, + + // Profiles + getProfiles, }; diff --git a/services/blockchain-indexer/shared/dataService/profiles.js b/services/blockchain-indexer/shared/dataService/profiles.js new file mode 100644 index 000000000..a6741da7a --- /dev/null +++ b/services/blockchain-indexer/shared/dataService/profiles.js @@ -0,0 +1,22 @@ +const { Logger } = require('lisk-service-framework'); +const util = require('util'); + +const logger = Logger(); + +const business = require('./business'); + +const getProfiles = async params => { + // Store logs + if (params.profileID) logger.debug(`Retrieved profile with ID ${params.profileID} from Lisk Core`); + else if (params.creatorAddress) logger.debug(`Retrieved profile with creatorAddress: ${params.creatorAddress} from Lisk Core`); + else logger.debug(`Retrieved profile with custom search: ${util.inspect(params)} from Lisk Core`); + + // Get data from server + const response = await business.getProfiles(params); + + return response; +}; + +module.exports = { + getProfiles, +}; From 1757937d8ec1e900e5d8574538a4d7755947af37 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Tue, 6 Jun 2023 22:38:41 +0330 Subject: [PATCH 54/79] Add profile database schema to indexer --- .../shared/database/schema/profiles.js | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 services/blockchain-indexer/shared/database/schema/profiles.js diff --git a/services/blockchain-indexer/shared/database/schema/profiles.js b/services/blockchain-indexer/shared/database/schema/profiles.js new file mode 100644 index 000000000..36b608b13 --- /dev/null +++ b/services/blockchain-indexer/shared/database/schema/profiles.js @@ -0,0 +1,24 @@ +module.exports = { + tableName: 'profiles', + primaryKey: 'profileID', + schema: { + name: { type: 'string', null: true, defaultValue: null }, + nickName: { type: 'string', null: true, defaultValue: null }, + description: { type: 'string', null: true, defaultValue: null }, + // socialAccounts: SocialAccount[]; + socialAccounts: [{ + username: { type: 'string', null: true, defaultValue: null }, + platform: { type: 'integer', null: true, defaultValue: null }, + }], + avatarHash: { type: 'string', null: true, defaultValue: null }, + avatarSignature: { type: 'string', null: true, defaultValue: null }, + bannerHash: { type: 'string', null: true, defaultValue: null }, + bannerSignature: { type: 'string', null: true, defaultValue: null }, + // @TODO: creationDate: { type: 'string', null: true, defaultValue: null }, + creatorAddress: { type: 'string', null: true, defaultValue: null }, + }, + indexes: { + creatorAddress: { type: 'string' }, + }, + purge: {}, +}; From f7138565e0f38e74b271914833a5866ae5416c28 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Tue, 6 Jun 2023 22:40:01 +0330 Subject: [PATCH 55/79] Add profile transaction processor into indexer --- .../transactionProcessor/profile/create.js | 75 +++++++++++++++++++ .../transactionProcessor/profile/index.js | 6 ++ 2 files changed, 81 insertions(+) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/profile/index.js diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js new file mode 100644 index 000000000..a4048ef99 --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js @@ -0,0 +1,75 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); + +const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const accountsTableSchema = require('../../../database/schema/accounts'); +const collectionsTableSchema = require('../../../database/schema/collections'); +const { + MODULE_NAME_PROFILE, + EVENT_NAME_PROFILE_CREATED, +} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); + +const getAccountsTable = () => getTableInstance( + accountsTableSchema.tableName, + accountsTableSchema, + MYSQL_ENDPOINT, +); + +const getProfilesTable = () => getTableInstance( + collectionsTableSchema.tableName, + collectionsTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'create'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const accountsTable = await getAccountsTable(); + const profilesTable = await getProfilesTable(); + + const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); + + const account = { + address: senderAddress, + }; + + logger.trace(`Updating account index for the account with address ${account.address}.`); + await accountsTable.upsert(account, dbTrx); + logger.debug(`Updated account index for the account with address ${account.address}.`); + + logger.trace(`Indexing profiles with address ${account.address}.`); + + const { data: eventData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_PROFILE + && name === EVENT_NAME_PROFILE_CREATED, + ); + + const profile = { + ...eventData, + ...tx.params, + }; + console.log('eventData>>>', eventData); + console.log('tx.params>>>', tx.params); + + await profilesTable.upsert(profile, dbTrx); + logger.debug(`Indexed profile with ID ${eventData.profileID}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => {}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/index.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/index.js new file mode 100644 index 000000000..290c852a1 --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/index.js @@ -0,0 +1,6 @@ +// Module specific constants +const MODULE_NAME = 'profile'; + +module.exports = { + MODULE_NAME, +}; From a0ff73d69c30e8c59ac89c2a2168e0be625da971 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Tue, 6 Jun 2023 23:43:06 +0330 Subject: [PATCH 56/79] Add profile:create command support into gateway --- .../apis/http-version3/methods/profiles.js | 38 +++++++ .../apis/http-version3/swagger/apiJson.json | 4 + .../swagger/definitions/profiles.json | 107 ++++++++++++++++++ .../swagger/parameters/profiles.json | 19 ++++ .../sources/version3/mappings/profile.js | 16 +++ services/gateway/sources/version3/profiles.js | 30 +++++ .../gateway/tests/constants/generateDocs.js | 30 +++++ .../gateway/tests/constants/registerApi.js | 4 + 8 files changed, 248 insertions(+) create mode 100644 services/gateway/apis/http-version3/methods/profiles.js create mode 100644 services/gateway/apis/http-version3/swagger/definitions/profiles.json create mode 100644 services/gateway/apis/http-version3/swagger/parameters/profiles.json create mode 100644 services/gateway/sources/version3/mappings/profile.js create mode 100644 services/gateway/sources/version3/profiles.js diff --git a/services/gateway/apis/http-version3/methods/profiles.js b/services/gateway/apis/http-version3/methods/profiles.js new file mode 100644 index 000000000..116882b5c --- /dev/null +++ b/services/gateway/apis/http-version3/methods/profiles.js @@ -0,0 +1,38 @@ +const profilesSource = require('../../../sources/version3/profiles'); +const envelope = require('../../../sources/version3/mappings/stdEnvelope'); +const regex = require('../../../shared/regex'); +const { transformParams, response, getSwaggerDescription } = require('../../../shared/utils'); + +module.exports = { + version: '2.0', + swaggerApiPath: '/profiles', + rpcMethod: 'get.profiles', + tags: ['Profiles'], + params: { + creatorAddress: { optional: true, type: 'string', min: 3, max: 41, pattern: regex.ADDRESS_LISK32 }, + profileID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + }, + get schema() { + const profileSchema = {}; + profileSchema[this.swaggerApiPath] = { get: {} }; + profileSchema[this.swaggerApiPath].get.tags = this.tags; + profileSchema[this.swaggerApiPath].get.summary = 'Requests profiles data'; + profileSchema[this.swaggerApiPath].get.description = getSwaggerDescription({ + rpcMethod: this.rpcMethod, + description: 'Returns profiles data', + }); + profileSchema[this.swaggerApiPath].get.parameters = transformParams('profiles', this.params); + profileSchema[this.swaggerApiPath].get.responses = { + 200: { + description: 'Returns a list of profiles', + schema: { + $ref: '#/definitions/profilesWithEnvelope', + }, + }, + }; + Object.assign(profileSchema[this.swaggerApiPath].get.responses, response); + return profileSchema; + }, + source: profilesSource, + envelope, +}; diff --git a/services/gateway/apis/http-version3/swagger/apiJson.json b/services/gateway/apis/http-version3/swagger/apiJson.json index d93731cfc..6434e49f9 100644 --- a/services/gateway/apis/http-version3/swagger/apiJson.json +++ b/services/gateway/apis/http-version3/swagger/apiJson.json @@ -98,6 +98,10 @@ { "name": "Audio", "description": "Audio module related API calls." + }, + { + "name": "Profile", + "description": "Profile module related API calls." } ], "schemes": [ diff --git a/services/gateway/apis/http-version3/swagger/definitions/profiles.json b/services/gateway/apis/http-version3/swagger/definitions/profiles.json new file mode 100644 index 000000000..3bba369c2 --- /dev/null +++ b/services/gateway/apis/http-version3/swagger/definitions/profiles.json @@ -0,0 +1,107 @@ +{ + "profile": { + "type": "object", + "required": [ + "profileID", + "name", + "nickName", + "description", + "socialAccounts", + "avatarHash", + "avatarSignature", + "bannerHash", + "bannerSignature", + "creatorAddress" + ], + "properties": { + "profileID": { + "type": "string", + "format": "id", + "example": "f9593f101c4acafc3ede650ab4c10fa2ecb59b225813eddbbb17b47e96932e9b", + "minLength": 1, + "maxLength": 64, + "description": "Unique identifier of the profile.\nDerived from the profile hash." + }, + "name": { + "type": "string", + "description": "name of the profile" + }, + "nickName": { + "type": "string", + "description": "nickName of the profile" + }, + "description": { + "type": "string", + "description": "description of the profile" + }, + "socialAccounts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "username": { + "type": "string", + "description": "username of the social media platform" + }, + "platform": { + "type": "string", + "description": "name of the social media platform" + } + } + } + }, + "avatarHash": { + "type": "string", + "example": "hash of the profile avatar" + }, + "avatarSignature": { + "type": "string", + "example": "signature of the profile avatar" + }, + "bannerHash": { + "type": "string", + "example": "hash of the profile banner" + }, + "bannerSignature": { + "type": "string", + "example": "signature of the profile banner" + }, + "creatorAddress": { + "type": "string", + "example": "creator address of the profile" + } + } + }, + "ProfilesWithEnvelope": { + "type": "object", + "required": [ + "data", + "meta" + ], + "properties": { + "data": { + "description": "List of profiles", + "type": "array", + "items": { + "$ref": "#/definitions/Profiles" + } + }, + "meta": { + "$ref": "#/definitions/pagination" + } + } + }, + "serverErrorEnvelope": { + "type": "object", + "properties": { + "error": { + "type": "boolean", + "example": true + }, + "message": { + "type": "string", + "example": "Unable to reach a network node" + } + } + } +} diff --git a/services/gateway/apis/http-version3/swagger/parameters/profiles.json b/services/gateway/apis/http-version3/swagger/parameters/profiles.json new file mode 100644 index 000000000..488a2795c --- /dev/null +++ b/services/gateway/apis/http-version3/swagger/parameters/profiles.json @@ -0,0 +1,19 @@ +{ + "creatorAddress": { + "name": "creatorAddress", + "in": "query", + "description": "Lisk account address", + "type": "string", + "minLength": 3, + "maxLength": 41 + }, + "profileID": { + "name": "profileID", + "in": "query", + "description": "profile ID to query", + "type": "string", + "format": "id", + "minLength": 1, + "maxLength": 64 + } +} diff --git a/services/gateway/sources/version3/mappings/profile.js b/services/gateway/sources/version3/mappings/profile.js new file mode 100644 index 000000000..f17e9927a --- /dev/null +++ b/services/gateway/sources/version3/mappings/profile.js @@ -0,0 +1,16 @@ +module.exports = { + name: '=,string', + nickName: '=,string', + profileID: '=,string', + description: '=,string', + socialAccounts: ['socialAccounts', { + username: '=,string', + platform: '=,number', + }], + avatarHash: '=,string', + avatarSignature: '=,string', + bannerHash: '=,string', + bannerSignature: '=,string', + creatorAddress: '=,string', +}; + diff --git a/services/gateway/sources/version3/profiles.js b/services/gateway/sources/version3/profiles.js new file mode 100644 index 000000000..cb201fac2 --- /dev/null +++ b/services/gateway/sources/version3/profiles.js @@ -0,0 +1,30 @@ +const profile = require('./mappings/profile'); + +module.exports = { + type: 'moleculer', + method: 'indexer.profiles', + params: { + profileID: '=,string', + name: '=,string', + nickName: '=,string', + description: '=,string', + socialAccounts: ['socialAccounts', { + username: '=,string', + platform: '=,number', + }], + avatarHash: '=,string', + avatarSignature: '=,string', + bannerHash: '=,string', + bannerSignature: '=,string', + creatorAddress: '=,string', + }, + definition: { + data: ['data', profile], + meta: { + count: '=,number', + offset: '=,number', + total: '=,number', + }, + links: {}, + }, +}; diff --git a/services/gateway/tests/constants/generateDocs.js b/services/gateway/tests/constants/generateDocs.js index d86a223fd..47cb6219b 100644 --- a/services/gateway/tests/constants/generateDocs.js +++ b/services/gateway/tests/constants/generateDocs.js @@ -468,6 +468,36 @@ const createApiDocsExpectedResponse = { 'Collections', ], }, + '/profiles': { + get: { + description: 'Returns profiles data\n RPC => get.profiles', + parameters: [ + { + $ref: '#/parameters/creatorAddress', + }, + { + $ref: '#/parameters/profileID', + }, + ], + responses: { + 200: { + description: 'Returns a list of profiles', + schema: { + $ref: '#/definitions/profilesWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, + }, + }, + summary: 'Requests profiles data', + tags: [ + 'Profiles', + ], + }, }, '/events': { get: { diff --git a/services/gateway/tests/constants/registerApi.js b/services/gateway/tests/constants/registerApi.js index 8e72f877d..ba6cbabdb 100644 --- a/services/gateway/tests/constants/registerApi.js +++ b/services/gateway/tests/constants/registerApi.js @@ -26,6 +26,7 @@ const expectedResponseForRegisterHttpApi = { 'app-registry.blockchain.apps.meta.tokens.supported', 'indexer.blocks', 'indexer.collections', + 'indexer.profiles', 'indexer.events', 'fees.estimates', 'indexer.generators', @@ -62,6 +63,7 @@ const expectedResponseForRegisterHttpApi = { aliases: { 'GET blocks/assets': 'indexer.blocks.assets', 'GET collections': 'indexer.collections', + 'GET profiles': 'indexer.profiles', 'GET audios': 'indexer.audios', 'GET blockchain/apps': 'indexer.blockchain.apps', 'GET blockchain/apps/meta/list': 'app-registry.blockchain.apps.meta.list', @@ -120,6 +122,7 @@ const expectedResponseForRegisterRpcApi = { 'app-registry.blockchain.apps.meta.tokens.supported', 'indexer.blocks', 'indexer.collections', + 'indexer.profiles', 'indexer.events', 'fees.estimates', 'indexer.generators', @@ -154,6 +157,7 @@ const expectedResponseForRegisterRpcApi = { aliases: { 'get.blocks.assets': 'indexer.blocks.assets', 'get.collections': 'indexer.collections', + 'get.profiles': 'indexer.profiles', 'get.audios': 'indexer.audios', 'get.blockchain.apps': 'indexer.blockchain.apps', 'get.blockchain.apps.meta.list': 'app-registry.blockchain.apps.meta.list', From 3562ba26cee905ef383043b3cd2ddf3b1b85a816 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Tue, 6 Jun 2023 22:49:51 +0200 Subject: [PATCH 57/79] Add support for indexing audio:streamed transactions --- .../shared/sdk/constants/names.js | 2 + .../transactionProcessor/audio/stream.js | 74 +++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/audio/stream.js diff --git a/services/blockchain-connector/shared/sdk/constants/names.js b/services/blockchain-connector/shared/sdk/constants/names.js index d78f985c4..f2ed36f33 100644 --- a/services/blockchain-connector/shared/sdk/constants/names.js +++ b/services/blockchain-connector/shared/sdk/constants/names.js @@ -97,6 +97,7 @@ const EVENT_NAME_COLLECTION_CREATED = 'collectionCreated'; // Audios const MODULE_NAME_AUDIO = 'audio'; const EVENT_NAME_AUDIO_CREATED = 'audioCreated'; +const EVENT_NAME_AUDIO_STREAMED = 'audioStreamed'; module.exports = { MODULE_NAME_AUTH, @@ -169,4 +170,5 @@ module.exports = { MODULE_NAME_AUDIO, EVENT_NAME_AUDIO_CREATED, + EVENT_NAME_AUDIO_STREAMED, }; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/stream.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/stream.js new file mode 100644 index 000000000..415a9a77e --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/stream.js @@ -0,0 +1,74 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); +const BluebirdPromise = require('bluebird'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const ownersTableSchema = require('../../../database/schema/owners'); + +const { + MODULE_NAME_AUDIO, + EVENT_NAME_AUDIO_STREAMED, + EVENT_NAME_COMMAND_EXECUTION_RESULT, +} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); + +const getOwnersTable = () => getTableInstance( + ownersTableSchema.tableName, + ownersTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'stream'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const { data: commandExecutedData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_AUDIO + && name === EVENT_NAME_COMMAND_EXECUTION_RESULT, + ); + if (!commandExecutedData.success) { + return false; + } + + const ownersTable = await getOwnersTable(); + + const { audioID } = tx.params; + + // Use event data to get audioID + const { data: audioStreamedData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_AUDIO + && name === EVENT_NAME_AUDIO_STREAMED, + ); + + await BluebirdPromise.map( + audioStreamedData.owners, + async owner => { + const info = { + ...owner, + audioID, + }; + logger.trace(`Updating owner index for the account with address ${owner.address}.`); + await ownersTable.upsert(info, dbTrx); + logger.debug(`Updated owner index for the account with address ${owner.address}.`); + return true; + }, + { concurrency: audioStreamedData.owners.length }, + ); + + return true; +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => {}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From 3a737302f58e6004aea33ec42f563f5978a90148 Mon Sep 17 00:00:00 2001 From: curvesy Date: Wed, 7 Jun 2023 16:38:00 +0330 Subject: [PATCH 58/79] Fix a mistake in profile/create.js that inserts data into the collectionTable --- .../shared/indexer/transactionProcessor/profile/create.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js index a4048ef99..96bb9c218 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js @@ -11,7 +11,7 @@ const logger = Logger(); const MYSQL_ENDPOINT = config.endpoints.mysql; const accountsTableSchema = require('../../../database/schema/accounts'); -const collectionsTableSchema = require('../../../database/schema/collections'); +const ProfilesTableSchema = require('../../../database/schema/profiles'); const { MODULE_NAME_PROFILE, EVENT_NAME_PROFILE_CREATED, @@ -24,8 +24,8 @@ const getAccountsTable = () => getTableInstance( ); const getProfilesTable = () => getTableInstance( - collectionsTableSchema.tableName, - collectionsTableSchema, + ProfilesTableSchema.tableName, + ProfilesTableSchema, MYSQL_ENDPOINT, ); From cff45088dd4739a2166eb220569013e1024bacc9 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Thu, 8 Jun 2023 13:07:12 +0330 Subject: [PATCH 59/79] Modify Social Accounts object in Profile schema --- .../shared/dataService/business/profiles.js | 1 + .../shared/database/schema/profiles.js | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/services/blockchain-indexer/shared/dataService/business/profiles.js b/services/blockchain-indexer/shared/dataService/business/profiles.js index 303b9a7db..26322db0e 100644 --- a/services/blockchain-indexer/shared/dataService/business/profiles.js +++ b/services/blockchain-indexer/shared/dataService/business/profiles.js @@ -18,6 +18,7 @@ const getProfiles = async (params = {}) => { const resultSet = await profilesTable.find( { ...params, limit: params.limit || 10 }, ['profileID', 'name', 'nickName', 'description', 'socialAccounts', 'creatorAddress'], + // ['profileID', 'name', 'nickName', 'description', 'creatorAddress'], ); const result = { diff --git a/services/blockchain-indexer/shared/database/schema/profiles.js b/services/blockchain-indexer/shared/database/schema/profiles.js index 36b608b13..a5ee76a4f 100644 --- a/services/blockchain-indexer/shared/database/schema/profiles.js +++ b/services/blockchain-indexer/shared/database/schema/profiles.js @@ -2,14 +2,19 @@ module.exports = { tableName: 'profiles', primaryKey: 'profileID', schema: { + profileID: { type: 'string' }, name: { type: 'string', null: true, defaultValue: null }, nickName: { type: 'string', null: true, defaultValue: null }, description: { type: 'string', null: true, defaultValue: null }, - // socialAccounts: SocialAccount[]; - socialAccounts: [{ - username: { type: 'string', null: true, defaultValue: null }, - platform: { type: 'integer', null: true, defaultValue: null }, - }], + socialAccounts: { type: 'array', + items: { + type: 'object', + props: { + username: { type: 'string', null: true, defaultValue: null }, + platform: { type: 'integer', null: true, defaultValue: null }, + }, + }, + }, avatarHash: { type: 'string', null: true, defaultValue: null }, avatarSignature: { type: 'string', null: true, defaultValue: null }, bannerHash: { type: 'string', null: true, defaultValue: null }, From 9c01da71021b7be2584d43e58ae258f9babf9ab3 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Fri, 9 Jun 2023 10:49:02 +0330 Subject: [PATCH 60/79] Modify Profile Schema & Indexer & DataService in order to write in DB temporarily --- .../shared/dataService/business/profiles.js | 4 ++-- .../shared/database/schema/profiles.js | 19 ++++++++++--------- .../transactionProcessor/profile/create.js | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/services/blockchain-indexer/shared/dataService/business/profiles.js b/services/blockchain-indexer/shared/dataService/business/profiles.js index 26322db0e..f95d242a3 100644 --- a/services/blockchain-indexer/shared/dataService/business/profiles.js +++ b/services/blockchain-indexer/shared/dataService/business/profiles.js @@ -17,8 +17,8 @@ const getProfiles = async (params = {}) => { const total = await profilesTable.count(params); const resultSet = await profilesTable.find( { ...params, limit: params.limit || 10 }, - ['profileID', 'name', 'nickName', 'description', 'socialAccounts', 'creatorAddress'], - // ['profileID', 'name', 'nickName', 'description', 'creatorAddress'], + // ['profileID', 'name', 'nickName', 'description', 'socialAccounts', 'creatorAddress'], + ['profileID', 'name', 'nickName', 'description', 'creatorAddress'], ); const result = { diff --git a/services/blockchain-indexer/shared/database/schema/profiles.js b/services/blockchain-indexer/shared/database/schema/profiles.js index a5ee76a4f..6b1903e70 100644 --- a/services/blockchain-indexer/shared/database/schema/profiles.js +++ b/services/blockchain-indexer/shared/database/schema/profiles.js @@ -6,15 +6,16 @@ module.exports = { name: { type: 'string', null: true, defaultValue: null }, nickName: { type: 'string', null: true, defaultValue: null }, description: { type: 'string', null: true, defaultValue: null }, - socialAccounts: { type: 'array', - items: { - type: 'object', - props: { - username: { type: 'string', null: true, defaultValue: null }, - platform: { type: 'integer', null: true, defaultValue: null }, - }, - }, - }, + // @TODO: Modify Schema in order to support social media accounts + // socialAccounts: { type: 'array', + // items: { + // type: 'object', + // props: { + // username: { type: 'string', null: true, defaultValue: null }, + // platform: { type: 'integer', null: true, defaultValue: null }, + // }, + // }, + // }, avatarHash: { type: 'string', null: true, defaultValue: null }, avatarSignature: { type: 'string', null: true, defaultValue: null }, bannerHash: { type: 'string', null: true, defaultValue: null }, diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js index 96bb9c218..a7fb23486 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js @@ -36,7 +36,7 @@ const COMMAND_NAME = 'create'; const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const accountsTable = await getAccountsTable(); const profilesTable = await getProfilesTable(); - + console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>> Start to write"); const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); const account = { From 9f15e0e9497febc187090fd18cf0365b06a9f209 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Fri, 9 Jun 2023 11:04:27 +0330 Subject: [PATCH 61/79] Correct linting error --- services/gateway/tests/constants/generateDocs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/gateway/tests/constants/generateDocs.js b/services/gateway/tests/constants/generateDocs.js index 47cb6219b..1f6ae408b 100644 --- a/services/gateway/tests/constants/generateDocs.js +++ b/services/gateway/tests/constants/generateDocs.js @@ -1123,8 +1123,8 @@ const createApiDocsExpectedResponse = { }, }, }, -}; +} module.exports = { createApiDocsExpectedResponse, -}; +} From 2142f5fded1f30c77f87669869bfc815aed5ef05 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Sat, 10 Jun 2023 23:10:08 +0330 Subject: [PATCH 62/79] Add social accounts schema to profile --- .../shared/sdk/constants/names.js | 2 +- .../shared/dataService/business/profiles.js | 31 ++++++++++++++-- .../shared/database/schema/socialAccounts.js | 13 +++++++ .../transactionProcessor/profile/create.js | 35 +++++++++++++++---- .../gateway/tests/constants/generateDocs.js | 4 +-- 5 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 services/blockchain-indexer/shared/database/schema/socialAccounts.js diff --git a/services/blockchain-connector/shared/sdk/constants/names.js b/services/blockchain-connector/shared/sdk/constants/names.js index f328047d2..1e0331a2d 100644 --- a/services/blockchain-connector/shared/sdk/constants/names.js +++ b/services/blockchain-connector/shared/sdk/constants/names.js @@ -100,7 +100,7 @@ const EVENT_NAME_COLLECTION_TRANSFERED = 'collectionTransfered'; const MODULE_NAME_AUDIO = 'audio'; const EVENT_NAME_AUDIO_CREATED = 'audioCreated'; -// Audios +// Profiles const MODULE_NAME_PROFILE = 'profile'; const EVENT_NAME_PROFILE_CREATED = 'profileCreated'; diff --git a/services/blockchain-indexer/shared/dataService/business/profiles.js b/services/blockchain-indexer/shared/dataService/business/profiles.js index f95d242a3..a8349379d 100644 --- a/services/blockchain-indexer/shared/dataService/business/profiles.js +++ b/services/blockchain-indexer/shared/dataService/business/profiles.js @@ -1,7 +1,9 @@ const { MySQL: { getTableInstance }, } = require('lisk-service-framework'); +const BluebirdPromise = require('bluebird'); const transactionsIndexSchema = require('../../database/schema/profiles'); +const socialAccountsIndexSchema = require('../../database/schema/socialAccounts'); const config = require('../../../config'); const MYSQL_ENDPOINT = config.endpoints.mysql; @@ -12,19 +14,42 @@ const getProfilesIndex = () => getTableInstance( MYSQL_ENDPOINT, ); +const getSocialAccountsIndex = () => getTableInstance( + socialAccountsIndexSchema.tableName, + socialAccountsIndexSchema, + MYSQL_ENDPOINT, +); + const getProfiles = async (params = {}) => { const profilesTable = await getProfilesIndex(); const total = await profilesTable.count(params); - const resultSet = await profilesTable.find( + const profilesData = await profilesTable.find( { ...params, limit: params.limit || 10 }, // ['profileID', 'name', 'nickName', 'description', 'socialAccounts', 'creatorAddress'], ['profileID', 'name', 'nickName', 'description', 'creatorAddress'], ); + const socialAccountsTable = await getSocialAccountsIndex(); + + const data = await BluebirdPromise.map( + profilesData, + async (profile) => { + const socialData = await socialAccountsTable.find( + { profileID: profile.profileID }, + ['profileID', 'username', 'platform'], + ); + + return { + ...profile, + socialAccounts: socialData, + }; + }, + { concurrency: profilesData.length }, + ); const result = { - data: resultSet, + data, meta: { - count: resultSet.length, + count: data.length, offset: parseInt(params.offset, 10) || 0, total, }, diff --git a/services/blockchain-indexer/shared/database/schema/socialAccounts.js b/services/blockchain-indexer/shared/database/schema/socialAccounts.js new file mode 100644 index 000000000..c36801869 --- /dev/null +++ b/services/blockchain-indexer/shared/database/schema/socialAccounts.js @@ -0,0 +1,13 @@ +module.exports = { + tableName: 'socialAccounts', + primaryKey: 'profileID', + schema: { + profileID: { type: 'string' }, + username: { type: 'string', null: true, defaultValue: null }, + platform: { type: 'string', null: true, defaultValue: null }, + }, + indexes: { + profileID: { type: 'string' }, + }, + purge: {}, +}; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js index a7fb23486..00d78dc15 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js @@ -2,6 +2,7 @@ const { Logger, MySQL: { getTableInstance }, } = require('lisk-service-framework'); +const BluebirdPromise = require('bluebird'); const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); @@ -11,7 +12,9 @@ const logger = Logger(); const MYSQL_ENDPOINT = config.endpoints.mysql; const accountsTableSchema = require('../../../database/schema/accounts'); -const ProfilesTableSchema = require('../../../database/schema/profiles'); +const profilesTableSchema = require('../../../database/schema/profiles'); +const socialAccountsTableSchema = require('../../../database/schema/socialAccounts'); + const { MODULE_NAME_PROFILE, EVENT_NAME_PROFILE_CREATED, @@ -24,8 +27,14 @@ const getAccountsTable = () => getTableInstance( ); const getProfilesTable = () => getTableInstance( - ProfilesTableSchema.tableName, - ProfilesTableSchema, + profilesTableSchema.tableName, + profilesTableSchema, + MYSQL_ENDPOINT, +); + +const getSocialAccountsTable = () => getTableInstance( + socialAccountsTableSchema.tableName, + socialAccountsTableSchema, MYSQL_ENDPOINT, ); @@ -36,7 +45,7 @@ const COMMAND_NAME = 'create'; const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const accountsTable = await getAccountsTable(); const profilesTable = await getProfilesTable(); - console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>> Start to write"); + const socialAccountsTable = await getSocialAccountsTable(); const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); const account = { @@ -58,8 +67,22 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { ...eventData, ...tx.params, }; - console.log('eventData>>>', eventData); - console.log('tx.params>>>', tx.params); + + await BluebirdPromise.map( + tx.params.socialAccounts, + async socialAccount => { + const socialInfo = { + profileID: eventData.profileID, + username: socialAccount.username, + platform: socialAccount.platform, + }; + logger.trace(`Inserting social sccounts for the profile with ID ${eventData.profileID}.`); + await socialAccountsTable.upsert(socialInfo, dbTrx); + logger.debug(`Inserted social sccounts for the profile with ID ${eventData.profileID}.`); + return true; + }, + { concurrency: tx.params.socialAccounts.length }, + ); await profilesTable.upsert(profile, dbTrx); logger.debug(`Indexed profile with ID ${eventData.profileID}.`); diff --git a/services/gateway/tests/constants/generateDocs.js b/services/gateway/tests/constants/generateDocs.js index 1f6ae408b..47cb6219b 100644 --- a/services/gateway/tests/constants/generateDocs.js +++ b/services/gateway/tests/constants/generateDocs.js @@ -1123,8 +1123,8 @@ const createApiDocsExpectedResponse = { }, }, }, -} +}; module.exports = { createApiDocsExpectedResponse, -} +}; From 66e44ab76fc285a08dd7f715a868b9453a59e725 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Sat, 10 Jun 2023 23:37:45 +0200 Subject: [PATCH 63/79] Add audio reclaim command support --- .../shared/sdk/constants/eventTopics.js | 4 + .../shared/sdk/constants/names.js | 2 + .../transactionProcessor/audio/create.js | 5 +- .../transactionProcessor/audio/reclaim.js | 76 +++++++++++++++++++ 4 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/audio/reclaim.js diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index dab3a6f22..1cc7ca4f2 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -83,6 +83,8 @@ const { MODULE_NAME_AUDIO, EVENT_NAME_AUDIO_CREATED, + EVENT_NAME_AUDIO_STREAMED, + EVENT_NAME_AUDIO_INCOME_RECLAIMED, } = require('./names'); const COMMAND_EXECUTION_RESULT_TOPICS = ['transactionID']; @@ -159,6 +161,8 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { }, [MODULE_NAME_AUDIO]: { [EVENT_NAME_AUDIO_CREATED]: ['transactionID', 'senderAddress'], + [EVENT_NAME_AUDIO_STREAMED]: ['transactionID', 'senderAddress'], + [EVENT_NAME_AUDIO_INCOME_RECLAIMED]: ['transactionID', 'senderAddress'], }, }; diff --git a/services/blockchain-connector/shared/sdk/constants/names.js b/services/blockchain-connector/shared/sdk/constants/names.js index f2ed36f33..76371b77e 100644 --- a/services/blockchain-connector/shared/sdk/constants/names.js +++ b/services/blockchain-connector/shared/sdk/constants/names.js @@ -98,6 +98,7 @@ const EVENT_NAME_COLLECTION_CREATED = 'collectionCreated'; const MODULE_NAME_AUDIO = 'audio'; const EVENT_NAME_AUDIO_CREATED = 'audioCreated'; const EVENT_NAME_AUDIO_STREAMED = 'audioStreamed'; +const EVENT_NAME_AUDIO_INCOME_RECLAIMED = 'audioIncomeReclaimed'; module.exports = { MODULE_NAME_AUTH, @@ -171,4 +172,5 @@ module.exports = { MODULE_NAME_AUDIO, EVENT_NAME_AUDIO_CREATED, EVENT_NAME_AUDIO_STREAMED, + EVENT_NAME_AUDIO_INCOME_RECLAIMED, }; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js index 375af16f2..7cac0c64a 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/create.js @@ -65,10 +65,11 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const featsTable = await getFeatsTable(); // Use event data to get audioID - const { data: audioCreatedData = {} } = events.find( + const eventData = events.find( ({ module, name }) => module === MODULE_NAME_AUDIO && name === EVENT_NAME_AUDIO_CREATED, ); + const { data: audioCreatedData } = eventData || { data: {} }; const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); @@ -127,7 +128,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { }; await audiosTable.upsert(audiosNFT, dbTrx); - logger.debug(`Indexed audio with ID ${audioCreatedData.audiosID}.`); + logger.debug(`Indexed audio with ID ${audioCreatedData.audioID}.`); return true; }; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/reclaim.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/reclaim.js new file mode 100644 index 000000000..9e377b4eb --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/audio/reclaim.js @@ -0,0 +1,76 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); +const BluebirdPromise = require('bluebird'); + +const config = require('../../../../config'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const ownersTableSchema = require('../../../database/schema/owners'); + +const { + MODULE_NAME_AUDIO, + EVENT_NAME_COMMAND_EXECUTION_RESULT, + EVENT_NAME_AUDIO_INCOME_RECLAIMED, +} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); + +const getOwnersTable = () => getTableInstance( + ownersTableSchema.tableName, + ownersTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'reclaim'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const { data: commandExecutedData = {} } = events.find( + ({ module, name }) => module === MODULE_NAME_AUDIO + && name === EVENT_NAME_COMMAND_EXECUTION_RESULT, + ); + if (!commandExecutedData.success) { + return false; + } + + const ownersTable = await getOwnersTable(); + + // Use event data to get audioID + const eventData = events.find( + ({ module, name }) => module === MODULE_NAME_AUDIO + && name === EVENT_NAME_AUDIO_INCOME_RECLAIMED, + ); + const { data: audioIncomeReclaimedData } = eventData || { data: { claimData: { audioIDs: [] } } }; + + await BluebirdPromise.map( + audioIncomeReclaimedData.claimData.audioIDs, + async audioID => { + const owners = await ownersTable.find({ audioID }, ['address', 'shares', 'income'], dbTrx); + const sender = owners.find(owner => owner.address === tx.senderAddress); + const info = { + ...sender, + audioID, + income: 0, + }; + logger.trace(`Updating owner index for the account with address ${sender.address} and audioID: ${audioID}.`); + await ownersTable.upsert(info, dbTrx); + logger.debug(`Updated owner index for the account with address ${sender.address} and audioID: ${audioID}.`); + return true; + }, + { concurrency: audioIncomeReclaimedData.claimData.audioIDs.length }, + ); + + return true; +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => {}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From 76c15aabae37e6b161d668c06b76b971c08e7f33 Mon Sep 17 00:00:00 2001 From: curvesy Date: Mon, 12 Jun 2023 16:36:19 +0330 Subject: [PATCH 64/79] Remove the unnecessary find CollectionID from collectionTable in collection destroy command --- .../transactionProcessor/collection/destroy.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js index 6416e0254..e398fb407 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/destroy.js @@ -22,15 +22,11 @@ const COMMAND_NAME = 'destroy'; // eslint-disable-next-line no-unused-vars const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const collectionsTable = await getCollectionsTable(); - const [collectionNFT] = await collectionsTable.find( - { collectionID: tx.params.collectionID }, - ['collectionID'], - dbTrx, - ); - - logger.trace(`Deleting collection with ID ${collectionNFT.collectionID}.`); - await collectionsTable.delete(collectionNFT, dbTrx); - logger.debug(`Deleted collection with ID ${collectionNFT.collectionsID}.`); + const { collectionID } = tx.params; + + logger.trace(`Deleting collection with ID ${collectionID}.`); + await collectionsTable.delete({ collectionID }, dbTrx); + logger.debug(`Deleted collection with ID ${collectionID}.`); }; // eslint-disable-next-line no-unused-vars From 9060bdbf876c698928203a6bcee0dba5e00cc970 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Tue, 13 Jun 2023 19:11:43 +0330 Subject: [PATCH 65/79] Fix issues with linting --- .../gateway/tests/constants/generateDocs.js | 1071 +++++++++-------- 1 file changed, 536 insertions(+), 535 deletions(-) diff --git a/services/gateway/tests/constants/generateDocs.js b/services/gateway/tests/constants/generateDocs.js index 47cb6219b..593e87e71 100644 --- a/services/gateway/tests/constants/generateDocs.js +++ b/services/gateway/tests/constants/generateDocs.js @@ -498,626 +498,627 @@ const createApiDocsExpectedResponse = { 'Profiles', ], }, - }, - '/events': { - get: { - tags: [ - 'Events', - ], - summary: 'Requests events data', - description: 'Returns events data\n RPC => get.events', - parameters: [ - { - $ref: '#/parameters/transactionID', - }, - { - $ref: '#/parameters/senderAddress', - }, - { - $ref: '#/parameters/topic', - }, - { - $ref: '#/parameters/blockID', - }, - { - $ref: '#/parameters/height', - }, - { - $ref: '#/parameters/timestamp', - }, - { - $ref: '#/parameters/limit', - }, - { - $ref: '#/parameters/offset', - }, - { - name: 'sort', - in: 'query', - description: 'Fields to sort results by.', - required: false, - type: 'string', - enum: [ - 'height:asc', - 'height:desc', - 'timestamp:asc', - 'timestamp:desc', - ], - default: 'timestamp:desc', - }, - { - name: 'order', - in: 'query', - description: 'Fields to order results by. The order condition is applied after the sort condition, usually to break ties when the sort condition results in collision.', - required: false, - type: 'string', - enum: [ - 'index:asc', - 'index:desc', - ], - default: 'index:asc', - }, - ], - responses: { - 200: { - description: 'Returns a list of events', - schema: { - $ref: '#/definitions/eventsWithEnvelope', + }, + '/events': { + get: { + tags: [ + 'Events', + ], + summary: 'Requests events data', + description: 'Returns events data\n RPC => get.events', + parameters: [ + { + $ref: '#/parameters/transactionID', }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + { + $ref: '#/parameters/senderAddress', + }, + { + $ref: '#/parameters/topic', + }, + { + $ref: '#/parameters/blockID', + }, + { + $ref: '#/parameters/height', + }, + { + $ref: '#/parameters/timestamp', + }, + { + $ref: '#/parameters/limit', + }, + { + $ref: '#/parameters/offset', + }, + { + name: 'sort', + in: 'query', + description: 'Fields to sort results by.', + required: false, + type: 'string', + enum: [ + 'height:asc', + 'height:desc', + 'timestamp:asc', + 'timestamp:desc', + ], + default: 'timestamp:desc', + }, + { + name: 'order', + in: 'query', + description: 'Fields to order results by. The order condition is applied after the sort condition, usually to break ties when the sort condition results in collision.', + required: false, + type: 'string', + enum: [ + 'index:asc', + 'index:desc', + ], + default: 'index:asc', + }, + ], + responses: { + 200: { + description: 'Returns a list of events', + schema: { + $ref: '#/definitions/eventsWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, }, }, }, }, - }, - '/fees': { - get: { - tags: [ - 'Fee', - ], - summary: 'Requests fee estimates', - description: 'Returns fee estimates\n RPC => get.fees', - responses: { - 200: { - description: 'Returns the fee estimate per byte used for transaction fee calculation', - schema: { - $ref: '#/definitions/FeeEstimateEnvelope', + '/fees': { + get: { + tags: [ + 'Fee', + ], + summary: 'Requests fee estimates', + description: 'Returns fee estimates\n RPC => get.fees', + responses: { + 200: { + description: 'Returns the fee estimate per byte used for transaction fee calculation', + schema: { + $ref: '#/definitions/FeeEstimateEnvelope', + }, }, }, }, }, - }, - '/generators': { - get: { - tags: [ - 'Generators', - ], - summary: 'Requests generators list', - description: 'Returns generators list\n RPC => get.generators', - parameters: [ - { - $ref: '#/parameters/limit', - }, - { - $ref: '#/parameters/offset', - }, - ], - responses: { - 200: { - description: 'Returns a list of generators', - schema: { - $ref: '#/definitions/generatorsWithEnvelope', + '/generators': { + get: { + tags: [ + 'Generators', + ], + summary: 'Requests generators list', + description: 'Returns generators list\n RPC => get.generators', + parameters: [ + { + $ref: '#/parameters/limit', }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + { + $ref: '#/parameters/offset', + }, + ], + responses: { + 200: { + description: 'Returns a list of generators', + schema: { + $ref: '#/definitions/generatorsWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, }, }, }, }, - }, - '/index/status': { - get: { - tags: [ - 'Index Status', - ], - summary: 'Requests current indexing status.', - description: 'Returns current indexing status.\n RPC => get.index.status', - responses: { - 200: { - description: 'Returns the current index status information.', - schema: { - $ref: '#/definitions/IndexStatus', + '/index/status': { + get: { + tags: [ + 'Index Status', + ], + summary: 'Requests current indexing status.', + description: 'Returns current indexing status.\n RPC => get.index.status', + responses: { + 200: { + description: 'Returns the current index status information.', + schema: { + $ref: '#/definitions/IndexStatus', + }, }, }, }, }, - }, - '/invoke': { - post: { - tags: [ - 'Proxy', - ], - summary: 'Proxy request to directly invoke application endpoint', - description: 'Returns endpoint response from the blockchain application in its original form.\n RPC => post.invoke', - parameters: [ - { - $ref: '#/parameters/invokeParams', - }, - ], - responses: { - 200: { - description: 'Returns endpoint response from the blockchain application in its original form.', - schema: { - $ref: '#/definitions/invokeWithEnvelope', + '/invoke': { + post: { + tags: [ + 'Proxy', + ], + summary: 'Proxy request to directly invoke application endpoint', + description: 'Returns endpoint response from the blockchain application in its original form.\n RPC => post.invoke', + parameters: [ + { + $ref: '#/parameters/invokeParams', }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + ], + responses: { + 200: { + description: 'Returns endpoint response from the blockchain application in its original form.', + schema: { + $ref: '#/definitions/invokeWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, }, }, }, }, - }, - '/market/prices': { - get: { - tags: [ - 'Market', - ], - parameters: [ + '/market/prices': { + get: { + tags: [ + 'Market', + ], + parameters: [ - ], - summary: 'Requests market prices', - description: 'Returns market prices\n RPC => get.market.prices', - responses: { - 200: { - description: 'Returns a list of market prices by currency pairs', - schema: { - $ref: '#/definitions/MarketPricesWithEnvelope', + ], + summary: 'Requests market prices', + description: 'Returns market prices\n RPC => get.market.prices', + responses: { + 200: { + description: 'Returns a list of market prices by currency pairs', + schema: { + $ref: '#/definitions/MarketPricesWithEnvelope', + }, }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, }, }, }, }, - }, - '/network/statistics': { - get: { - tags: [ - 'Network', - ], - summary: 'Requests network statistics', - description: 'Returns network statistics data\n RPC => get.network.statistics', - responses: { - 200: { - description: 'Returns the network statistics information', - schema: { - $ref: '#/definitions/NetworkStatistics', + '/network/statistics': { + get: { + tags: [ + 'Network', + ], + summary: 'Requests network statistics', + description: 'Returns network statistics data\n RPC => get.network.statistics', + responses: { + 200: { + description: 'Returns the network statistics information', + schema: { + $ref: '#/definitions/NetworkStatistics', + }, }, }, }, }, - }, - '/network/status': { - get: { - tags: [ - 'Network', - ], - summary: 'Requests network status', - description: 'Returns network status\n RPC => get.network.status', - responses: { - 200: { - description: 'Returns the network status information', - schema: { - $ref: '#/definitions/NetworkStatus', + '/network/status': { + get: { + tags: [ + 'Network', + ], + summary: 'Requests network status', + description: 'Returns network status\n RPC => get.network.status', + responses: { + 200: { + description: 'Returns the network status information', + schema: { + $ref: '#/definitions/NetworkStatus', + }, }, }, }, }, - }, - '/network/peers': { - get: { - tags: [ - 'Network', - ], - summary: 'Requests peers data', - description: 'Returns peers data\n RPC => get.network.peers', - parameters: [ - { - $ref: '#/parameters/ip', - }, - { - $ref: '#/parameters/networkVersion', - }, - { - $ref: '#/parameters/state', - }, - { - $ref: '#/parameters/height', - }, - { - $ref: '#/parameters/limit', - }, - { - $ref: '#/parameters/offset', - }, - { - name: 'sort', - in: 'query', - description: 'Fields to sort results by.', - required: false, - type: 'string', - enum: [ - 'height:asc', - 'height:desc', - 'networkVersion:asc', - 'networkVersion:desc', - ], - default: 'height:desc', - }, - ], - responses: { - 200: { - description: 'Returns a list of peer nodes in the network', - schema: { - $ref: '#/definitions/PeersWithEnvelope', + '/network/peers': { + get: { + tags: [ + 'Network', + ], + summary: 'Requests peers data', + description: 'Returns peers data\n RPC => get.network.peers', + parameters: [ + { + $ref: '#/parameters/ip', }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + { + $ref: '#/parameters/networkVersion', + }, + { + $ref: '#/parameters/state', + }, + { + $ref: '#/parameters/height', + }, + { + $ref: '#/parameters/limit', + }, + { + $ref: '#/parameters/offset', + }, + { + name: 'sort', + in: 'query', + description: 'Fields to sort results by.', + required: false, + type: 'string', + enum: [ + 'height:asc', + 'height:desc', + 'networkVersion:asc', + 'networkVersion:desc', + ], + default: 'height:desc', + }, + ], + responses: { + 200: { + description: 'Returns a list of peer nodes in the network', + schema: { + $ref: '#/definitions/PeersWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, }, }, }, }, - }, - '/subscriptions': { - get: { - description: 'Returns subscriptions data\n RPC => get.subscriptions', - parameters: [ - { - $ref: '#/parameters/creatorAddress', - }, - { - $ref: '#/parameters/subscriptionID', - }, - ], - responses: { - 200: { - description: 'Returns a list of subscriptions', - schema: { - $ref: '#/definitions/subscriptionsWithEnvelope', + '/subscriptions': { + get: { + description: 'Returns subscriptions data\n RPC => get.subscriptions', + parameters: [ + { + $ref: '#/parameters/creatorAddress', }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + { + $ref: '#/parameters/subscriptionID', + }, + ], + responses: { + 200: { + description: 'Returns a list of subscriptions', + schema: { + $ref: '#/definitions/subscriptionsWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, }, }, + summary: 'Requests subscriptions data', + tags: [ + 'Subscriptions', + ], }, - summary: 'Requests subscriptions data', - tags: [ - 'Subscriptions', - ], }, - }, - '/transactions': { - get: { - tags: [ - 'Transactions', - ], - summary: 'Requests transactions data', - description: 'Returns transactions data\n RPC => get.transactions', - parameters: [ - { - $ref: '#/parameters/transactionID', - }, - { - $ref: '#/parameters/moduleCommand', - }, - { - $ref: '#/parameters/senderAddress', - }, - { - $ref: '#/parameters/address', - }, - { - $ref: '#/parameters/recipientAddress', - }, - { - $ref: '#/parameters/blockID', - }, - { - $ref: '#/parameters/height', - }, - { - $ref: '#/parameters/timestamp', - }, - { - $ref: '#/parameters/executionStatus', - }, - { - $ref: '#/parameters/nonce', - }, - { - $ref: '#/parameters/limit', - }, - { - $ref: '#/parameters/offset', - }, - { - name: 'sort', - in: 'query', - description: 'Fields to sort results by.', - required: false, - type: 'string', - enum: [ - 'height:asc', - 'height:desc', - 'timestamp:asc', - 'timestamp:desc', - ], - default: 'timestamp:desc', - }, - { - name: 'order', - in: 'query', - description: 'Fields to order results by. The order condition is applied after the sort condition, usually to break ties when the sort condition results in collision.', - required: false, - type: 'string', - enum: [ - 'index:asc', - 'index:desc', - ], - default: 'index:asc', - }, - ], - responses: { - 200: { - description: 'Returns a list of transactions', - schema: { - $ref: '#/definitions/TransactionsWithEnvelope', + '/transactions': { + get: { + tags: [ + 'Transactions', + ], + summary: 'Requests transactions data', + description: 'Returns transactions data\n RPC => get.transactions', + parameters: [ + { + $ref: '#/parameters/transactionID', + }, + { + $ref: '#/parameters/moduleCommand', + }, + { + $ref: '#/parameters/senderAddress', + }, + { + $ref: '#/parameters/address', + }, + { + $ref: '#/parameters/recipientAddress', + }, + { + $ref: '#/parameters/blockID', + }, + { + $ref: '#/parameters/height', + }, + { + $ref: '#/parameters/timestamp', + }, + { + $ref: '#/parameters/executionStatus', + }, + { + $ref: '#/parameters/nonce', + }, + { + $ref: '#/parameters/limit', }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + { + $ref: '#/parameters/offset', + }, + { + name: 'sort', + in: 'query', + description: 'Fields to sort results by.', + required: false, + type: 'string', + enum: [ + 'height:asc', + 'height:desc', + 'timestamp:asc', + 'timestamp:desc', + ], + default: 'timestamp:desc', + }, + { + name: 'order', + in: 'query', + description: 'Fields to order results by. The order condition is applied after the sort condition, usually to break ties when the sort condition results in collision.', + required: false, + type: 'string', + enum: [ + 'index:asc', + 'index:desc', + ], + default: 'index:asc', + }, + ], + responses: { + 200: { + description: 'Returns a list of transactions', + schema: { + $ref: '#/definitions/TransactionsWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, }, }, }, - }, - post: { - tags: [ - 'Transactions', - ], - summary: 'Post transactions', - description: 'Post transactions and return transactionID\n RPC => post.transactions', - parameters: [ - { - $ref: '#/parameters/transaction', - }, - ], - responses: { - 200: { - description: 'Broadcast transaction', - schema: { - $ref: '#/definitions/postTransactionWithEnvelope', + post: { + tags: [ + 'Transactions', + ], + summary: 'Post transactions', + description: 'Post transactions and return transactionID\n RPC => post.transactions', + parameters: [ + { + $ref: '#/parameters/transaction', }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequestEnvelope', + ], + responses: { + 200: { + description: 'Broadcast transaction', + schema: { + $ref: '#/definitions/postTransactionWithEnvelope', + }, }, - }, - 500: { - description: 'Internal server error', - schema: { - $ref: '#/definitions/serverErrorEnvelope', + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequestEnvelope', + }, + }, + 500: { + description: 'Internal server error', + schema: { + $ref: '#/definitions/serverErrorEnvelope', + }, }, }, }, }, - }, - '/schemas': { - get: { - tags: [ - 'Schemas', - ], - summary: 'Requests schemas.', - description: 'Returns schemas.\n RPC => get.schemas', - responses: { - 200: { - description: 'Returns a list of schemas.', - schema: { - $ref: '#/definitions/SchemaWithEnvelope', + '/schemas': { + get: { + tags: [ + 'Schemas', + ], + summary: 'Requests schemas.', + description: 'Returns schemas.\n RPC => get.schemas', + responses: { + 200: { + description: 'Returns a list of schemas.', + schema: { + $ref: '#/definitions/SchemaWithEnvelope', + }, }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, }, }, }, }, - }, - '/transactions/dryrun': { - post: { - tags: [ - 'Transactions', - ], - summary: 'Dry run transactions.', - description: 'Dry run transactions.\n RPC => post.transactions.dryrun', - parameters: [ - { - $ref: '#/parameters/dryrunTransaction', - }, - ], - responses: { - 200: { - description: "Dry run transactions. 'errorMessage' is available only when 'result: -1'.", - schema: { - $ref: '#/definitions/dryTransactionWithEnvelope', + '/transactions/dryrun': { + post: { + tags: [ + 'Transactions', + ], + summary: 'Dry run transactions.', + description: 'Dry run transactions.\n RPC => post.transactions.dryrun', + parameters: [ + { + $ref: '#/parameters/dryrunTransaction', }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + ], + responses: { + 200: { + description: "Dry run transactions. 'errorMessage' is available only when 'result: -1'.", + schema: { + $ref: '#/definitions/dryTransactionWithEnvelope', + }, }, - }, - 500: { - description: 'Internal server error', - schema: { - $ref: '#/definitions/serverErrorEnvelope', + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, + }, + 500: { + description: 'Internal server error', + schema: { + $ref: '#/definitions/serverErrorEnvelope', + }, }, }, }, }, - }, - '/transactions/statistics': { - get: { - tags: [ - 'Transactions', - ], - summary: 'Requests transaction statistics', - description: 'Returns transaction statistics\n RPC => get.transactions.statistics', - parameters: [ - { - name: 'interval', - in: 'query', - description: 'interval to query statistics', - required: true, - type: 'string', - enum: [ - 'day', - 'month', - ], - }, - { - $ref: '#/parameters/limit', - }, - { - $ref: '#/parameters/offset', - }, - ], - responses: { - 200: { - description: 'Returns a list of transactions statistics by date or month', - schema: { - $ref: '#/definitions/TransactionsStatisticsWithEnvelope', + '/transactions/statistics': { + get: { + tags: [ + 'Transactions', + ], + summary: 'Requests transaction statistics', + description: 'Returns transaction statistics\n RPC => get.transactions.statistics', + parameters: [ + { + name: 'interval', + in: 'query', + description: 'interval to query statistics', + required: true, + type: 'string', + enum: [ + 'day', + 'month', + ], }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + { + $ref: '#/parameters/limit', }, - }, - 503: { - description: 'Service Unavailable', - schema: { - $ref: '#/definitions/serviceUnavailable', + { + $ref: '#/parameters/offset', + }, + ], + responses: { + 200: { + description: 'Returns a list of transactions statistics by date or month', + schema: { + $ref: '#/definitions/TransactionsStatisticsWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, + }, + 503: { + description: 'Service Unavailable', + schema: { + $ref: '#/definitions/serviceUnavailable', + }, }, }, }, }, - }, - '/auth': { - get: { - tags: [ - 'Auth', - ], - summary: 'Requests auth details by address', - description: 'Returns auth details by address\n RPC => get.auth', - parameters: [ - { - $ref: '#/parameters/address', - }, - ], - responses: { - 200: { - description: 'Auth details', - schema: { - $ref: '#/definitions/authWithEnvelope', + '/auth': { + get: { + tags: [ + 'Auth', + ], + summary: 'Requests auth details by address', + description: 'Returns auth details by address\n RPC => get.auth', + parameters: [ + { + $ref: '#/parameters/address', }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + ], + responses: { + 200: { + description: 'Auth details', + schema: { + $ref: '#/definitions/authWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, }, }, }, }, - }, - '/validator': { - get: { - tags: [ - 'Validator', - ], - summary: 'Requests validator information', - description: 'Returns validator information\n RPC => get.validator', - parameters: [ - { - $ref: '#/parameters/address', - }, - ], - responses: { - 200: { - description: 'Returns validator information by address', - schema: { - $ref: '#/definitions/validatorWithEnvelope', + '/validator': { + get: { + tags: [ + 'Validator', + ], + summary: 'Requests validator information', + description: 'Returns validator information\n RPC => get.validator', + parameters: [ + { + $ref: '#/parameters/address', }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + ], + responses: { + 200: { + description: 'Returns validator information by address', + schema: { + $ref: '#/definitions/validatorWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, }, }, }, }, - }, - '/validator/validate-bls-key': { - post: { - tags: [ - 'Validator', - ], - summary: 'Validates a BLS key against its corresponding Proof of Possession.', - description: 'Validates a BLS key against its corresponding Proof of Possession.\n RPC => post.validator.validate-bls-key', - parameters: [ - { - $ref: '#/parameters/validateBLSKeyParams', - }, - ], - responses: { - 200: { - description: 'Returns a boolean representing the validity of the supplied BLS key and Proof of Possession.', - schema: { - $ref: '#/definitions/blsKeyValidationWithEnvelope', + '/validator/validate-bls-key': { + post: { + tags: [ + 'Validator', + ], + summary: 'Validates a BLS key against its corresponding Proof of Possession.', + description: 'Validates a BLS key against its corresponding Proof of Possession.\n RPC => post.validator.validate-bls-key', + parameters: [ + { + $ref: '#/parameters/validateBLSKeyParams', }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', + ], + responses: { + 200: { + description: 'Returns a boolean representing the validity of the supplied BLS key and Proof of Possession.', + schema: { + $ref: '#/definitions/blsKeyValidationWithEnvelope', + }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', + }, }, }, }, From 93fc54fc95cdaa2e2e975814a71af28b04116a0a Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Tue, 13 Jun 2023 19:03:52 +0200 Subject: [PATCH 66/79] Fix broken unit tests --- .../gateway/tests/constants/generateDocs.js | 1126 ++++++++--------- .../gateway/tests/constants/registerApi.js | 6 +- 2 files changed, 566 insertions(+), 566 deletions(-) diff --git a/services/gateway/tests/constants/generateDocs.js b/services/gateway/tests/constants/generateDocs.js index 593e87e71..cf57676dd 100644 --- a/services/gateway/tests/constants/generateDocs.js +++ b/services/gateway/tests/constants/generateDocs.js @@ -468,657 +468,657 @@ const createApiDocsExpectedResponse = { 'Collections', ], }, - '/profiles': { - get: { - description: 'Returns profiles data\n RPC => get.profiles', - parameters: [ - { - $ref: '#/parameters/creatorAddress', - }, - { - $ref: '#/parameters/profileID', - }, - ], - responses: { - 200: { - description: 'Returns a list of profiles', - schema: { - $ref: '#/definitions/profilesWithEnvelope', - }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, - }, + }, + '/events': { + get: { + tags: [ + 'Events', + ], + summary: 'Requests events data', + description: 'Returns events data\n RPC => get.events', + parameters: [ + { + $ref: '#/parameters/transactionID', }, - summary: 'Requests profiles data', - tags: [ - 'Profiles', - ], - }, - }, - '/events': { - get: { - tags: [ - 'Events', - ], - summary: 'Requests events data', - description: 'Returns events data\n RPC => get.events', - parameters: [ - { - $ref: '#/parameters/transactionID', - }, - { - $ref: '#/parameters/senderAddress', - }, - { - $ref: '#/parameters/topic', - }, - { - $ref: '#/parameters/blockID', - }, - { - $ref: '#/parameters/height', - }, - { - $ref: '#/parameters/timestamp', - }, - { - $ref: '#/parameters/limit', - }, - { - $ref: '#/parameters/offset', - }, - { - name: 'sort', - in: 'query', - description: 'Fields to sort results by.', - required: false, - type: 'string', - enum: [ - 'height:asc', - 'height:desc', - 'timestamp:asc', - 'timestamp:desc', - ], - default: 'timestamp:desc', - }, - { - name: 'order', - in: 'query', - description: 'Fields to order results by. The order condition is applied after the sort condition, usually to break ties when the sort condition results in collision.', - required: false, - type: 'string', - enum: [ - 'index:asc', - 'index:desc', - ], - default: 'index:asc', - }, - ], - responses: { - 200: { - description: 'Returns a list of events', - schema: { - $ref: '#/definitions/eventsWithEnvelope', - }, + { + $ref: '#/parameters/senderAddress', + }, + { + $ref: '#/parameters/topic', + }, + { + $ref: '#/parameters/blockID', + }, + { + $ref: '#/parameters/height', + }, + { + $ref: '#/parameters/timestamp', + }, + { + $ref: '#/parameters/limit', + }, + { + $ref: '#/parameters/offset', + }, + { + name: 'sort', + in: 'query', + description: 'Fields to sort results by.', + required: false, + type: 'string', + enum: [ + 'height:asc', + 'height:desc', + 'timestamp:asc', + 'timestamp:desc', + ], + default: 'timestamp:desc', + }, + { + name: 'order', + in: 'query', + description: 'Fields to order results by. The order condition is applied after the sort condition, usually to break ties when the sort condition results in collision.', + required: false, + type: 'string', + enum: [ + 'index:asc', + 'index:desc', + ], + default: 'index:asc', + }, + ], + responses: { + 200: { + description: 'Returns a list of events', + schema: { + $ref: '#/definitions/eventsWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, }, }, - '/fees': { - get: { - tags: [ - 'Fee', - ], - summary: 'Requests fee estimates', - description: 'Returns fee estimates\n RPC => get.fees', - responses: { - 200: { - description: 'Returns the fee estimate per byte used for transaction fee calculation', - schema: { - $ref: '#/definitions/FeeEstimateEnvelope', - }, + }, + '/fees': { + get: { + tags: [ + 'Fee', + ], + summary: 'Requests fee estimates', + description: 'Returns fee estimates\n RPC => get.fees', + responses: { + 200: { + description: 'Returns the fee estimate per byte used for transaction fee calculation', + schema: { + $ref: '#/definitions/FeeEstimateEnvelope', }, }, }, }, - '/generators': { - get: { - tags: [ - 'Generators', - ], - summary: 'Requests generators list', - description: 'Returns generators list\n RPC => get.generators', - parameters: [ - { - $ref: '#/parameters/limit', - }, - { - $ref: '#/parameters/offset', - }, - ], - responses: { - 200: { - description: 'Returns a list of generators', - schema: { - $ref: '#/definitions/generatorsWithEnvelope', - }, + }, + '/generators': { + get: { + tags: [ + 'Generators', + ], + summary: 'Requests generators list', + description: 'Returns generators list\n RPC => get.generators', + parameters: [ + { + $ref: '#/parameters/limit', + }, + { + $ref: '#/parameters/offset', + }, + ], + responses: { + 200: { + description: 'Returns a list of generators', + schema: { + $ref: '#/definitions/generatorsWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, }, }, - '/index/status': { - get: { - tags: [ - 'Index Status', - ], - summary: 'Requests current indexing status.', - description: 'Returns current indexing status.\n RPC => get.index.status', - responses: { - 200: { - description: 'Returns the current index status information.', - schema: { - $ref: '#/definitions/IndexStatus', - }, + }, + '/index/status': { + get: { + tags: [ + 'Index Status', + ], + summary: 'Requests current indexing status.', + description: 'Returns current indexing status.\n RPC => get.index.status', + responses: { + 200: { + description: 'Returns the current index status information.', + schema: { + $ref: '#/definitions/IndexStatus', }, }, }, }, - '/invoke': { - post: { - tags: [ - 'Proxy', - ], - summary: 'Proxy request to directly invoke application endpoint', - description: 'Returns endpoint response from the blockchain application in its original form.\n RPC => post.invoke', - parameters: [ - { - $ref: '#/parameters/invokeParams', - }, - ], - responses: { - 200: { - description: 'Returns endpoint response from the blockchain application in its original form.', - schema: { - $ref: '#/definitions/invokeWithEnvelope', - }, + }, + '/invoke': { + post: { + tags: [ + 'Proxy', + ], + summary: 'Proxy request to directly invoke application endpoint', + description: 'Returns endpoint response from the blockchain application in its original form.\n RPC => post.invoke', + parameters: [ + { + $ref: '#/parameters/invokeParams', + }, + ], + responses: { + 200: { + description: 'Returns endpoint response from the blockchain application in its original form.', + schema: { + $ref: '#/definitions/invokeWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, }, }, - '/market/prices': { - get: { - tags: [ - 'Market', - ], - parameters: [ + }, + '/market/prices': { + get: { + tags: [ + 'Market', + ], + parameters: [ - ], - summary: 'Requests market prices', - description: 'Returns market prices\n RPC => get.market.prices', - responses: { - 200: { - description: 'Returns a list of market prices by currency pairs', - schema: { - $ref: '#/definitions/MarketPricesWithEnvelope', - }, + ], + summary: 'Requests market prices', + description: 'Returns market prices\n RPC => get.market.prices', + responses: { + 200: { + description: 'Returns a list of market prices by currency pairs', + schema: { + $ref: '#/definitions/MarketPricesWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, }, }, - '/network/statistics': { - get: { - tags: [ - 'Network', - ], - summary: 'Requests network statistics', - description: 'Returns network statistics data\n RPC => get.network.statistics', - responses: { - 200: { - description: 'Returns the network statistics information', - schema: { - $ref: '#/definitions/NetworkStatistics', - }, + }, + '/network/statistics': { + get: { + tags: [ + 'Network', + ], + summary: 'Requests network statistics', + description: 'Returns network statistics data\n RPC => get.network.statistics', + responses: { + 200: { + description: 'Returns the network statistics information', + schema: { + $ref: '#/definitions/NetworkStatistics', }, }, }, }, - '/network/status': { - get: { - tags: [ - 'Network', - ], - summary: 'Requests network status', - description: 'Returns network status\n RPC => get.network.status', - responses: { - 200: { - description: 'Returns the network status information', - schema: { - $ref: '#/definitions/NetworkStatus', - }, + }, + '/network/status': { + get: { + tags: [ + 'Network', + ], + summary: 'Requests network status', + description: 'Returns network status\n RPC => get.network.status', + responses: { + 200: { + description: 'Returns the network status information', + schema: { + $ref: '#/definitions/NetworkStatus', }, }, }, }, - '/network/peers': { - get: { - tags: [ - 'Network', - ], - summary: 'Requests peers data', - description: 'Returns peers data\n RPC => get.network.peers', - parameters: [ - { - $ref: '#/parameters/ip', - }, - { - $ref: '#/parameters/networkVersion', - }, - { - $ref: '#/parameters/state', - }, - { - $ref: '#/parameters/height', - }, - { - $ref: '#/parameters/limit', - }, - { - $ref: '#/parameters/offset', - }, - { - name: 'sort', - in: 'query', - description: 'Fields to sort results by.', - required: false, - type: 'string', - enum: [ - 'height:asc', - 'height:desc', - 'networkVersion:asc', - 'networkVersion:desc', - ], - default: 'height:desc', - }, - ], - responses: { - 200: { - description: 'Returns a list of peer nodes in the network', - schema: { - $ref: '#/definitions/PeersWithEnvelope', - }, + }, + '/network/peers': { + get: { + tags: [ + 'Network', + ], + summary: 'Requests peers data', + description: 'Returns peers data\n RPC => get.network.peers', + parameters: [ + { + $ref: '#/parameters/ip', + }, + { + $ref: '#/parameters/networkVersion', + }, + { + $ref: '#/parameters/state', + }, + { + $ref: '#/parameters/height', + }, + { + $ref: '#/parameters/limit', + }, + { + $ref: '#/parameters/offset', + }, + { + name: 'sort', + in: 'query', + description: 'Fields to sort results by.', + required: false, + type: 'string', + enum: [ + 'height:asc', + 'height:desc', + 'networkVersion:asc', + 'networkVersion:desc', + ], + default: 'height:desc', + }, + ], + responses: { + 200: { + description: 'Returns a list of peer nodes in the network', + schema: { + $ref: '#/definitions/PeersWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, }, }, - '/subscriptions': { - get: { - description: 'Returns subscriptions data\n RPC => get.subscriptions', - parameters: [ - { - $ref: '#/parameters/creatorAddress', - }, - { - $ref: '#/parameters/subscriptionID', - }, - ], - responses: { - 200: { - description: 'Returns a list of subscriptions', - schema: { - $ref: '#/definitions/subscriptionsWithEnvelope', - }, + }, + '/subscriptions': { + get: { + description: 'Returns subscriptions data\n RPC => get.subscriptions', + parameters: [ + { + $ref: '#/parameters/creatorAddress', + }, + { + $ref: '#/parameters/subscriptionID', + }, + ], + responses: { + 200: { + description: 'Returns a list of subscriptions', + schema: { + $ref: '#/definitions/subscriptionsWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, - summary: 'Requests subscriptions data', - tags: [ - 'Subscriptions', - ], }, + summary: 'Requests subscriptions data', + tags: [ + 'Subscriptions', + ], }, - '/transactions': { - get: { - tags: [ - 'Transactions', - ], - summary: 'Requests transactions data', - description: 'Returns transactions data\n RPC => get.transactions', - parameters: [ - { - $ref: '#/parameters/transactionID', - }, - { - $ref: '#/parameters/moduleCommand', - }, - { - $ref: '#/parameters/senderAddress', - }, - { - $ref: '#/parameters/address', - }, - { - $ref: '#/parameters/recipientAddress', - }, - { - $ref: '#/parameters/blockID', - }, - { - $ref: '#/parameters/height', - }, - { - $ref: '#/parameters/timestamp', - }, - { - $ref: '#/parameters/executionStatus', - }, - { - $ref: '#/parameters/nonce', - }, - { - $ref: '#/parameters/limit', - }, - { - $ref: '#/parameters/offset', - }, - { - name: 'sort', - in: 'query', - description: 'Fields to sort results by.', - required: false, - type: 'string', - enum: [ - 'height:asc', - 'height:desc', - 'timestamp:asc', - 'timestamp:desc', - ], - default: 'timestamp:desc', - }, - { - name: 'order', - in: 'query', - description: 'Fields to order results by. The order condition is applied after the sort condition, usually to break ties when the sort condition results in collision.', - required: false, - type: 'string', - enum: [ - 'index:asc', - 'index:desc', - ], - default: 'index:asc', - }, - ], - responses: { - 200: { - description: 'Returns a list of transactions', - schema: { - $ref: '#/definitions/TransactionsWithEnvelope', - }, + }, + '/transactions': { + get: { + tags: [ + 'Transactions', + ], + summary: 'Requests transactions data', + description: 'Returns transactions data\n RPC => get.transactions', + parameters: [ + { + $ref: '#/parameters/transactionID', + }, + { + $ref: '#/parameters/moduleCommand', + }, + { + $ref: '#/parameters/senderAddress', + }, + { + $ref: '#/parameters/address', + }, + { + $ref: '#/parameters/recipientAddress', + }, + { + $ref: '#/parameters/blockID', + }, + { + $ref: '#/parameters/height', + }, + { + $ref: '#/parameters/timestamp', + }, + { + $ref: '#/parameters/executionStatus', + }, + { + $ref: '#/parameters/nonce', + }, + { + $ref: '#/parameters/limit', + }, + { + $ref: '#/parameters/offset', + }, + { + name: 'sort', + in: 'query', + description: 'Fields to sort results by.', + required: false, + type: 'string', + enum: [ + 'height:asc', + 'height:desc', + 'timestamp:asc', + 'timestamp:desc', + ], + default: 'timestamp:desc', + }, + { + name: 'order', + in: 'query', + description: 'Fields to order results by. The order condition is applied after the sort condition, usually to break ties when the sort condition results in collision.', + required: false, + type: 'string', + enum: [ + 'index:asc', + 'index:desc', + ], + default: 'index:asc', + }, + ], + responses: { + 200: { + description: 'Returns a list of transactions', + schema: { + $ref: '#/definitions/TransactionsWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, }, - post: { - tags: [ - 'Transactions', - ], - summary: 'Post transactions', - description: 'Post transactions and return transactionID\n RPC => post.transactions', - parameters: [ - { - $ref: '#/parameters/transaction', - }, - ], - responses: { - 200: { - description: 'Broadcast transaction', - schema: { - $ref: '#/definitions/postTransactionWithEnvelope', - }, + }, + post: { + tags: [ + 'Transactions', + ], + summary: 'Post transactions', + description: 'Post transactions and return transactionID\n RPC => post.transactions', + parameters: [ + { + $ref: '#/parameters/transaction', + }, + ], + responses: { + 200: { + description: 'Broadcast transaction', + schema: { + $ref: '#/definitions/postTransactionWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequestEnvelope', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequestEnvelope', }, - 500: { - description: 'Internal server error', - schema: { - $ref: '#/definitions/serverErrorEnvelope', - }, + }, + 500: { + description: 'Internal server error', + schema: { + $ref: '#/definitions/serverErrorEnvelope', }, }, }, }, - '/schemas': { - get: { - tags: [ - 'Schemas', - ], - summary: 'Requests schemas.', - description: 'Returns schemas.\n RPC => get.schemas', - responses: { - 200: { - description: 'Returns a list of schemas.', - schema: { - $ref: '#/definitions/SchemaWithEnvelope', - }, + }, + '/profiles': { + get: { + description: 'Returns profiles data\n RPC => get.profiles', + parameters: [ + { + $ref: '#/parameters/creatorAddress', + }, + { + $ref: '#/parameters/profileID', + }, + ], + responses: { + 200: { + description: 'Returns a list of profiles', + schema: { + $ref: '#/definitions/profilesWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, }, + summary: 'Requests profiles data', + tags: [ + 'Profiles', + ], }, - '/transactions/dryrun': { - post: { - tags: [ - 'Transactions', - ], - summary: 'Dry run transactions.', - description: 'Dry run transactions.\n RPC => post.transactions.dryrun', - parameters: [ - { - $ref: '#/parameters/dryrunTransaction', - }, - ], - responses: { - 200: { - description: "Dry run transactions. 'errorMessage' is available only when 'result: -1'.", - schema: { - $ref: '#/definitions/dryTransactionWithEnvelope', - }, - }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + '/schemas': { + get: { + tags: [ + 'Schemas', + ], + summary: 'Requests schemas.', + description: 'Returns schemas.\n RPC => get.schemas', + responses: { + 200: { + description: 'Returns a list of schemas.', + schema: { + $ref: '#/definitions/SchemaWithEnvelope', }, - 500: { - description: 'Internal server error', - schema: { - $ref: '#/definitions/serverErrorEnvelope', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, }, }, - '/transactions/statistics': { - get: { - tags: [ - 'Transactions', - ], - summary: 'Requests transaction statistics', - description: 'Returns transaction statistics\n RPC => get.transactions.statistics', - parameters: [ - { - name: 'interval', - in: 'query', - description: 'interval to query statistics', - required: true, - type: 'string', - enum: [ - 'day', - 'month', - ], + }, + '/transactions/dryrun': { + post: { + tags: [ + 'Transactions', + ], + summary: 'Dry run transactions.', + description: 'Dry run transactions.\n RPC => post.transactions.dryrun', + parameters: [ + { + $ref: '#/parameters/dryrunTransaction', + }, + ], + responses: { + 200: { + description: "Dry run transactions. 'errorMessage' is available only when 'result: -1'.", + schema: { + $ref: '#/definitions/dryTransactionWithEnvelope', }, - { - $ref: '#/parameters/limit', + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, - { - $ref: '#/parameters/offset', + }, + 500: { + description: 'Internal server error', + schema: { + $ref: '#/definitions/serverErrorEnvelope', }, - ], - responses: { - 200: { - description: 'Returns a list of transactions statistics by date or month', - schema: { - $ref: '#/definitions/TransactionsStatisticsWithEnvelope', - }, + }, + }, + }, + }, + '/transactions/statistics': { + get: { + tags: [ + 'Transactions', + ], + summary: 'Requests transaction statistics', + description: 'Returns transaction statistics\n RPC => get.transactions.statistics', + parameters: [ + { + name: 'interval', + in: 'query', + description: 'interval to query statistics', + required: true, + type: 'string', + enum: [ + 'day', + 'month', + ], + }, + { + $ref: '#/parameters/limit', + }, + { + $ref: '#/parameters/offset', + }, + ], + responses: { + 200: { + description: 'Returns a list of transactions statistics by date or month', + schema: { + $ref: '#/definitions/TransactionsStatisticsWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, - 503: { - description: 'Service Unavailable', - schema: { - $ref: '#/definitions/serviceUnavailable', - }, + }, + 503: { + description: 'Service Unavailable', + schema: { + $ref: '#/definitions/serviceUnavailable', }, }, }, }, - '/auth': { - get: { - tags: [ - 'Auth', - ], - summary: 'Requests auth details by address', - description: 'Returns auth details by address\n RPC => get.auth', - parameters: [ - { - $ref: '#/parameters/address', - }, - ], - responses: { - 200: { - description: 'Auth details', - schema: { - $ref: '#/definitions/authWithEnvelope', - }, + }, + '/auth': { + get: { + tags: [ + 'Auth', + ], + summary: 'Requests auth details by address', + description: 'Returns auth details by address\n RPC => get.auth', + parameters: [ + { + $ref: '#/parameters/address', + }, + ], + responses: { + 200: { + description: 'Auth details', + schema: { + $ref: '#/definitions/authWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, }, }, - '/validator': { - get: { - tags: [ - 'Validator', - ], - summary: 'Requests validator information', - description: 'Returns validator information\n RPC => get.validator', - parameters: [ - { - $ref: '#/parameters/address', - }, - ], - responses: { - 200: { - description: 'Returns validator information by address', - schema: { - $ref: '#/definitions/validatorWithEnvelope', - }, + }, + '/validator': { + get: { + tags: [ + 'Validator', + ], + summary: 'Requests validator information', + description: 'Returns validator information\n RPC => get.validator', + parameters: [ + { + $ref: '#/parameters/address', + }, + ], + responses: { + 200: { + description: 'Returns validator information by address', + schema: { + $ref: '#/definitions/validatorWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, }, }, - '/validator/validate-bls-key': { - post: { - tags: [ - 'Validator', - ], - summary: 'Validates a BLS key against its corresponding Proof of Possession.', - description: 'Validates a BLS key against its corresponding Proof of Possession.\n RPC => post.validator.validate-bls-key', - parameters: [ - { - $ref: '#/parameters/validateBLSKeyParams', - }, - ], - responses: { - 200: { - description: 'Returns a boolean representing the validity of the supplied BLS key and Proof of Possession.', - schema: { - $ref: '#/definitions/blsKeyValidationWithEnvelope', - }, + }, + '/validator/validate-bls-key': { + post: { + tags: [ + 'Validator', + ], + summary: 'Validates a BLS key against its corresponding Proof of Possession.', + description: 'Validates a BLS key against its corresponding Proof of Possession.\n RPC => post.validator.validate-bls-key', + parameters: [ + { + $ref: '#/parameters/validateBLSKeyParams', + }, + ], + responses: { + 200: { + description: 'Returns a boolean representing the validity of the supplied BLS key and Proof of Possession.', + schema: { + $ref: '#/definitions/blsKeyValidationWithEnvelope', }, - 400: { - description: 'Bad request', - schema: { - $ref: '#/definitions/badRequest', - }, + }, + 400: { + description: 'Bad request', + schema: { + $ref: '#/definitions/badRequest', }, }, }, diff --git a/services/gateway/tests/constants/registerApi.js b/services/gateway/tests/constants/registerApi.js index ba6cbabdb..c11d29b83 100644 --- a/services/gateway/tests/constants/registerApi.js +++ b/services/gateway/tests/constants/registerApi.js @@ -26,7 +26,6 @@ const expectedResponseForRegisterHttpApi = { 'app-registry.blockchain.apps.meta.tokens.supported', 'indexer.blocks', 'indexer.collections', - 'indexer.profiles', 'indexer.events', 'fees.estimates', 'indexer.generators', @@ -37,6 +36,7 @@ const expectedResponseForRegisterHttpApi = { 'indexer.network.statistics', 'indexer.network.status', 'indexer.transactions.post', + 'indexer.profiles', 'indexer.schemas', 'gateway.spec', 'indexer.subscriptions', @@ -122,7 +122,6 @@ const expectedResponseForRegisterRpcApi = { 'app-registry.blockchain.apps.meta.tokens.supported', 'indexer.blocks', 'indexer.collections', - 'indexer.profiles', 'indexer.events', 'fees.estimates', 'indexer.generators', @@ -133,6 +132,7 @@ const expectedResponseForRegisterRpcApi = { 'indexer.network.statistics', 'indexer.network.status', 'indexer.transactions.post', + 'indexer.profiles', 'indexer.schemas', 'indexer.subscriptions', 'indexer.transactions', @@ -157,7 +157,6 @@ const expectedResponseForRegisterRpcApi = { aliases: { 'get.blocks.assets': 'indexer.blocks.assets', 'get.collections': 'indexer.collections', - 'get.profiles': 'indexer.profiles', 'get.audios': 'indexer.audios', 'get.blockchain.apps': 'indexer.blockchain.apps', 'get.blockchain.apps.meta.list': 'app-registry.blockchain.apps.meta.list', @@ -175,6 +174,7 @@ const expectedResponseForRegisterRpcApi = { 'get.network.peers': 'indexer.network.peers', 'get.network.statistics': 'indexer.network.statistics', 'get.network.status': 'indexer.network.status', + 'get.profiles': 'indexer.profiles', 'post.transactions': 'indexer.transactions.post', 'get.schemas': 'indexer.schemas', 'get.subscriptions': 'indexer.subscriptions', From 4831911d52f4e16ea6ae59738949086dffd2b62a Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Sun, 11 Jun 2023 22:56:16 +0330 Subject: [PATCH 67/79] Add Profile:SetAttribute Functionality --- .../shared/database/schema/profiles.js | 10 -- .../profile/setAttribute.js | 95 +++++++++++++++++++ 2 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js diff --git a/services/blockchain-indexer/shared/database/schema/profiles.js b/services/blockchain-indexer/shared/database/schema/profiles.js index 6b1903e70..54acb4120 100644 --- a/services/blockchain-indexer/shared/database/schema/profiles.js +++ b/services/blockchain-indexer/shared/database/schema/profiles.js @@ -6,16 +6,6 @@ module.exports = { name: { type: 'string', null: true, defaultValue: null }, nickName: { type: 'string', null: true, defaultValue: null }, description: { type: 'string', null: true, defaultValue: null }, - // @TODO: Modify Schema in order to support social media accounts - // socialAccounts: { type: 'array', - // items: { - // type: 'object', - // props: { - // username: { type: 'string', null: true, defaultValue: null }, - // platform: { type: 'integer', null: true, defaultValue: null }, - // }, - // }, - // }, avatarHash: { type: 'string', null: true, defaultValue: null }, avatarSignature: { type: 'string', null: true, defaultValue: null }, bannerHash: { type: 'string', null: true, defaultValue: null }, diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js new file mode 100644 index 000000000..20f8f7ac5 --- /dev/null +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js @@ -0,0 +1,95 @@ +const { + Logger, + MySQL: { getTableInstance }, +} = require('lisk-service-framework'); +const BluebirdPromise = require('bluebird'); +const config = require('../../../../config'); +const { getLisk32AddressFromPublicKey } = require('../../../utils/account'); + +const logger = Logger(); + +const MYSQL_ENDPOINT = config.endpoints.mysql; +const profilesTableSchema = require('../../../database/schema/profiles'); +const socialAccountsTableSchema = require('../../../database/schema/socialAccounts'); + +const getProfilesTable = () => getTableInstance( + profilesTableSchema.tableName, + profilesTableSchema, + MYSQL_ENDPOINT, +); +const getSocialAccountsTable = () => getTableInstance( + socialAccountsTableSchema.tableName, + socialAccountsTableSchema, + MYSQL_ENDPOINT, +); + +// Command specific constants +const COMMAND_NAME = 'setAttributes'; + +// eslint-disable-next-line no-unused-vars +const applyTransaction = async (blockHeader, tx, events, dbTrx) => { + const profilesTable = await getProfilesTable(); + const socialAccountsTable = await getSocialAccountsTable(); + const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); + + const account = { + address: senderAddress, + }; + logger.trace(`Indexing profiles with address ${account.address}.`); + const [existingProfile] = await profilesTable.find( + { profileID: tx.params.profileID }, + ['profileID', 'name', 'nickName', 'description', 'avatarHash', 'avatarSignature', 'bannerHash', 'bannerSignature', 'creatorAddress'], + dbTrx, + ); + if (!existingProfile) { + throw new Error(`Profile with ID ${tx.params.profileID} does not exist.`); + } + + const newProfile = { + // Copy existing profile properties + ...tx.params, + // Use the existing creatorAddress, prevent changing it + }; + newProfile.profileID = existingProfile.profileID; + newProfile.creatorAddress = existingProfile.creatorAddress; + + // // Copy properties from tx.params.profile dynamically + // Object.keys(tx.params.profile).forEach((property) => { + // if (property !== 'profileID' && property !== 'creatorAddress') { + // profile[property] = tx.params.profile[property]; + // } + // }); + // Delete existing social account records + await socialAccountsTable.delete({ profileID: existingProfile.profileID }, dbTrx); + + // Insert new social account records + await BluebirdPromise.map( + tx.params.socialAccounts, + async (socialAccount) => { + const socialInfo = { + profileID: existingProfile.profileID, + username: socialAccount.username, + platform: socialAccount.platform, + }; + logger.trace(`Updating social accounts for the profile with ID ${existingProfile.profileID}.`); + await socialAccountsTable.upsert(socialInfo, dbTrx); + logger.debug(`Updating social accounts for the profile with ID ${existingProfile.profileID}.`); + return true; + }, + { concurrency: tx.params.socialAccounts.length }, + ); + + // Update the profile record + + await profilesTable.upsert(newProfile, dbTrx); + logger.debug(`Updated profile with ID ${existingProfile.profileID}.`); +}; + +// eslint-disable-next-line no-unused-vars +const revertTransaction = async (blockHeader, tx, events, dbTrx) => {}; + +module.exports = { + COMMAND_NAME, + applyTransaction, + revertTransaction, +}; From ebef06077fc67f856f54138343262491ad42f481 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Tue, 13 Jun 2023 19:13:51 +0200 Subject: [PATCH 68/79] Minor syntax adjustments --- .../transactionProcessor/profile/setAttribute.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js index 20f8f7ac5..b0bb4c9ae 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js @@ -31,10 +31,11 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const profilesTable = await getProfilesTable(); const socialAccountsTable = await getSocialAccountsTable(); const senderAddress = getLisk32AddressFromPublicKey(tx.senderPublicKey); - + const account = { address: senderAddress, }; + logger.trace(`Indexing profiles with address ${account.address}.`); const [existingProfile] = await profilesTable.find( { profileID: tx.params.profileID }, @@ -46,19 +47,11 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { } const newProfile = { - // Copy existing profile properties ...tx.params, - // Use the existing creatorAddress, prevent changing it + profileID: existingProfile.profileID, + creatorAddress: existingProfile.creatorAddress, }; - newProfile.profileID = existingProfile.profileID; - newProfile.creatorAddress = existingProfile.creatorAddress; - // // Copy properties from tx.params.profile dynamically - // Object.keys(tx.params.profile).forEach((property) => { - // if (property !== 'profileID' && property !== 'creatorAddress') { - // profile[property] = tx.params.profile[property]; - // } - // }); // Delete existing social account records await socialAccountsTable.delete({ profileID: existingProfile.profileID }, dbTrx); @@ -80,7 +73,6 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { ); // Update the profile record - await profilesTable.upsert(newProfile, dbTrx); logger.debug(`Updated profile with ID ${existingProfile.profileID}.`); }; From e27703f1b035c61038849a99ac3bb222fa877b89 Mon Sep 17 00:00:00 2001 From: curvesy Date: Sun, 25 Jun 2023 20:33:02 +0330 Subject: [PATCH 69/79] Return audios by collectionID --- services/gateway/apis/http-version3/methods/audios.js | 1 + 1 file changed, 1 insertion(+) diff --git a/services/gateway/apis/http-version3/methods/audios.js b/services/gateway/apis/http-version3/methods/audios.js index 896f08588..e1d1eed73 100644 --- a/services/gateway/apis/http-version3/methods/audios.js +++ b/services/gateway/apis/http-version3/methods/audios.js @@ -11,6 +11,7 @@ module.exports = { params: { creatorAddress: { optional: true, type: 'string', min: 3, max: 41, pattern: regex.ADDRESS_LISK32 }, audioID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + collectionID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, }, get schema() { const audioSchema = {}; From 2ed9c913d69e4ecc03e1a46f0dff3a5614b9ccef Mon Sep 17 00:00:00 2001 From: curvesy Date: Mon, 26 Jun 2023 16:35:01 +0330 Subject: [PATCH 70/79] Return audios by ownerAddress --- .../shared/dataService/business/audios.js | 33 ++++++++++++++++--- .../apis/http-version3/methods/audios.js | 1 + 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/services/blockchain-indexer/shared/dataService/business/audios.js b/services/blockchain-indexer/shared/dataService/business/audios.js index d1f6b14bc..30785b9c6 100644 --- a/services/blockchain-indexer/shared/dataService/business/audios.js +++ b/services/blockchain-indexer/shared/dataService/business/audios.js @@ -41,11 +41,34 @@ const getAudios = async (params = {}) => { const ownersTable = await getOwnersIndex(); const featsTable = await getFeatsIndex(); - const total = await audiosTable.count(params); - const audioData = await audiosTable.find( - { ...params, limit: params.limit || total }, - ['audioID', 'creatorAddress', 'name', 'releaseYear', 'collectionID'], - ); + let audioData = []; + + if (params.ownerAddress) { + // audiosID + const audioIDs = await ownersTable.find( + { address: params.ownerAddress }, + ['audioID'], + ); + audioData = await BluebirdPromise.map( + audioIDs, + async (audioID) => { + const audio = await audiosTable.find( + { audioID: audioID.audioID }, + ['audioID', 'creatorAddress', 'name', 'releaseYear', 'collectionID'], + ); + + return audio[0]; + }, + { concurrency: audioIDs.length }, + ); + } else { + audioData = await audiosTable.find( + { ...params, limit: params.limit }, + ['audioID', 'creatorAddress', 'name', 'releaseYear', 'collectionID'], + ); + } + + const total = audioData.length; const data = await BluebirdPromise.map( audioData, diff --git a/services/gateway/apis/http-version3/methods/audios.js b/services/gateway/apis/http-version3/methods/audios.js index e1d1eed73..235fae4df 100644 --- a/services/gateway/apis/http-version3/methods/audios.js +++ b/services/gateway/apis/http-version3/methods/audios.js @@ -12,6 +12,7 @@ module.exports = { creatorAddress: { optional: true, type: 'string', min: 3, max: 41, pattern: regex.ADDRESS_LISK32 }, audioID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, collectionID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + ownerAddress: { optional: true, type: 'string', min: 3, max: 41, pattern: regex.ADDRESS_LISK32 }, }, get schema() { const audioSchema = {}; From fd68a2c05b40b14d7b12f6cc747a95fad923bc00 Mon Sep 17 00:00:00 2001 From: curvesy Date: Tue, 27 Jun 2023 16:04:16 +0330 Subject: [PATCH 71/79] Filter owner audios based on share greater than zero and fix the test issue --- .../shared/dataService/business/audios.js | 7 +++++-- services/gateway/tests/constants/generateDocs.js | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/services/blockchain-indexer/shared/dataService/business/audios.js b/services/blockchain-indexer/shared/dataService/business/audios.js index 30785b9c6..fe067d067 100644 --- a/services/blockchain-indexer/shared/dataService/business/audios.js +++ b/services/blockchain-indexer/shared/dataService/business/audios.js @@ -47,10 +47,13 @@ const getAudios = async (params = {}) => { // audiosID const audioIDs = await ownersTable.find( { address: params.ownerAddress }, - ['audioID'], + ['audioID', 'shares'], ); + + const filteredAudioIDs = audioIDs.filter(audio => audio.shares > 0); + audioData = await BluebirdPromise.map( - audioIDs, + filteredAudioIDs, async (audioID) => { const audio = await audiosTable.find( { audioID: audioID.audioID }, diff --git a/services/gateway/tests/constants/generateDocs.js b/services/gateway/tests/constants/generateDocs.js index cf57676dd..c65783f41 100644 --- a/services/gateway/tests/constants/generateDocs.js +++ b/services/gateway/tests/constants/generateDocs.js @@ -24,6 +24,12 @@ const createApiDocsExpectedResponse = { { $ref: '#/parameters/audioID', }, + { + $ref: '#/parameters/collectionID', + }, + { + $ref: '#/parameters/ownerAddress', + }, ], responses: { 200: { From 461423f106cddcbbf8cd3e5e5d3abfcb766357a9 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Wed, 28 Jun 2023 07:48:12 +0330 Subject: [PATCH 72/79] Modify subscriptions to return active subscritions of a member --- .../dataService/modules/subscription.js | 1 + .../dataService/business/subscriptions.js | 40 +++++++++++++++++++ .../shared/dataService/subscriptions.js | 1 + .../shared/database/schema/members.js | 10 ++--- .../shared/database/schema/subscriptions.js | 4 +- .../subscription/purchase.js | 4 +- .../http-version3/methods/subscriptions.js | 1 + .../gateway/tests/constants/generateDocs.js | 3 ++ 8 files changed, 55 insertions(+), 9 deletions(-) diff --git a/services/blockchain-indexer/methods/dataService/modules/subscription.js b/services/blockchain-indexer/methods/dataService/modules/subscription.js index 0d39391ce..f13ba86ed 100644 --- a/services/blockchain-indexer/methods/dataService/modules/subscription.js +++ b/services/blockchain-indexer/methods/dataService/modules/subscription.js @@ -11,6 +11,7 @@ module.exports = [ subscriptionID: { optional: true, type: 'string' }, limit: { optional: true, type: 'number' }, offset: { optional: true, type: 'number' }, + memberAddress: { optional: true, type: 'string' }, }, }, ]; diff --git a/services/blockchain-indexer/shared/dataService/business/subscriptions.js b/services/blockchain-indexer/shared/dataService/business/subscriptions.js index 07f67d287..5b87d4270 100644 --- a/services/blockchain-indexer/shared/dataService/business/subscriptions.js +++ b/services/blockchain-indexer/shared/dataService/business/subscriptions.js @@ -20,8 +20,48 @@ const getMembersIndex = () => getTableInstance( membersIndexSchema, MYSQL_ENDPOINT, ); +const getActiveSubscriptionsForMember = async (params = {}) => { + const membersTable = await getMembersIndex(); + const subscriptionsTable = await getSubscriptionsIndex(); + + // Find rows [project on 'shared' field] that has address == memberAddress && removedBy == null + const total = await subscriptionsTable.count(params); + const members = await membersTable.find( + { ...params, limit: params.limit || total }, + // ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'maxMembers', 'streams'], + ['id', 'address', 'shared', 'addedBy', 'removedBy'], + ); + // We assume that member is always on just 1 active subscription + // Find all subscriptions that (subscriptionID == 'shared') + const data = await BluebirdPromise.map( + members, + async member => { + const subscriptions = await subscriptionsTable.find( + { shared: member.shared }, // member[0] + ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'maxMembers', 'streams'], + ); + member.subscription = subscriptions.map( + subscription => ({ subscriptionID: subscription.shared })); + return member; + }, + { concurrency: members.length }, + ); + + const result = { + data, + meta: { + count: data.length, + offset: parseInt(params.offset, 10) || 0, + total, + }, + }; + return result; +}; const getSubscriptions = async (params = {}) => { + if (params.memberAddress !== null) { + return getActiveSubscriptionsForMember(params); + } const subscriptionsTable = await getSubscriptionsIndex(); const membersTable = await getMembersIndex(); diff --git a/services/blockchain-indexer/shared/dataService/subscriptions.js b/services/blockchain-indexer/shared/dataService/subscriptions.js index df99a6d0d..8f647cc2e 100644 --- a/services/blockchain-indexer/shared/dataService/subscriptions.js +++ b/services/blockchain-indexer/shared/dataService/subscriptions.js @@ -7,6 +7,7 @@ const business = require('./business'); const getSubscriptions = async params => { // Store logs + if (params.memberAddress) logger.debug(`Retrieved active subscription for member ${params.memberAddress} from Lisk Core`); if (params.subscriptionID) logger.debug(`Retrieved subscription with ID ${params.subscriptionID} from Lisk Core`); else if (params.creatorAddress) logger.debug(`Retrieved subscription with creatorAddress: ${params.creatorAddress} from Lisk Core`); else logger.debug(`Retrieved subscription with custom search: ${util.inspect(params)} from Lisk Core`); diff --git a/services/blockchain-indexer/shared/database/schema/members.js b/services/blockchain-indexer/shared/database/schema/members.js index de740138b..3f1aacd2a 100644 --- a/services/blockchain-indexer/shared/database/schema/members.js +++ b/services/blockchain-indexer/shared/database/schema/members.js @@ -2,11 +2,11 @@ module.exports = { tableName: 'members', primaryKey: 'id', schema: { - id: { type: 'string' }, - address: { type: 'string' }, - shared: { type: 'string', null: true }, - addedBy: { type: 'string', null: true }, - removedBy: { type: 'string', null: true }, + id: { type: 'string' }, // memberAddress-nonce + address: { type: 'string' }, // memberAddress + shared: { type: 'string', null: true }, // subscriptionID + addedBy: { type: 'string', null: true }, // transactionID + removedBy: { type: 'string', null: true }, // transactionID }, indexes: { shared: { type: 'key' }, diff --git a/services/blockchain-indexer/shared/database/schema/subscriptions.js b/services/blockchain-indexer/shared/database/schema/subscriptions.js index 20b179602..9246dbf16 100644 --- a/services/blockchain-indexer/shared/database/schema/subscriptions.js +++ b/services/blockchain-indexer/shared/database/schema/subscriptions.js @@ -3,14 +3,14 @@ module.exports = { primaryKey: 'subscriptionID', schema: { subscriptionID: { type: 'string', null: true, defaultValue: null }, - creatorAddress: { type: 'string', null: true, defaultValue: null }, + creatorAddress: { type: 'string', null: true, defaultValue: null }, // at first->DEV but then->user price: { type: 'bigInteger', null: true, defaultValue: null }, consumable: { type: 'bigInteger', null: true, defaultValue: null }, streams: { type: 'bigInteger', null: true, defaultValue: null }, maxMembers: { type: 'integer', null: true, defaultValue: null }, }, indexes: { - senderAddress: { type: 'string' }, + creatorAddress: { type: 'string' }, }, purge: {}, }; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js index 1ebd9035f..9a2c16f0a 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/subscription/purchase.js @@ -62,7 +62,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { async member => { const oldAccount = { address: member }; logger.trace(`Updating account index for the account with address ${member}.`); - await accountsTable.upsert(oldAccount, dbTrx); + await accountsTable.upsert(oldAccount, dbTrx);// Create account for that member if not exists logger.debug(`Updated account index for the account with address ${member}.`); const memberData = { @@ -71,7 +71,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { addedBy: tx.id, shared: subscriptionID, }; - + // subscription members logger.trace(`Updating member index for the member with address ${member}.`); await membersTable.upsert(memberData, dbTrx); logger.debug(`Updated member index for the member with address ${member}.`); diff --git a/services/gateway/apis/http-version3/methods/subscriptions.js b/services/gateway/apis/http-version3/methods/subscriptions.js index d3eb0d8ed..b65960bdd 100644 --- a/services/gateway/apis/http-version3/methods/subscriptions.js +++ b/services/gateway/apis/http-version3/methods/subscriptions.js @@ -27,6 +27,7 @@ module.exports = { params: { creatorAddress: { optional: true, type: 'string', min: 3, max: 41, pattern: regex.ADDRESS_LISK32 }, subscriptionID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, + memberAddress: { type: 'string', required: false }, }, get schema() { const subscriptionSchema = {}; diff --git a/services/gateway/tests/constants/generateDocs.js b/services/gateway/tests/constants/generateDocs.js index cf57676dd..aabda4221 100644 --- a/services/gateway/tests/constants/generateDocs.js +++ b/services/gateway/tests/constants/generateDocs.js @@ -764,6 +764,9 @@ const createApiDocsExpectedResponse = { { $ref: '#/parameters/subscriptionID', }, + { + $ref: '#/parameters/memberAddress', + }, ], responses: { 200: { From 3d994d80759a6664f5c5633e7c6118fdd729c729 Mon Sep 17 00:00:00 2001 From: Hamid HK Date: Fri, 30 Jun 2023 10:50:11 +0330 Subject: [PATCH 73/79] Modify subscriptions to filter by memberAddress --- .../dataService/business/subscriptions.js | 77 +++++++++++-------- .../http-version3/methods/subscriptions.js | 2 +- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/services/blockchain-indexer/shared/dataService/business/subscriptions.js b/services/blockchain-indexer/shared/dataService/business/subscriptions.js index 5b87d4270..6aa6fb0bf 100644 --- a/services/blockchain-indexer/shared/dataService/business/subscriptions.js +++ b/services/blockchain-indexer/shared/dataService/business/subscriptions.js @@ -20,48 +20,52 @@ const getMembersIndex = () => getTableInstance( membersIndexSchema, MYSQL_ENDPOINT, ); -const getActiveSubscriptionsForMember = async (params = {}) => { +const getActiveSubscriptionsByMemberAddress = async (params = {}) => { const membersTable = await getMembersIndex(); const subscriptionsTable = await getSubscriptionsIndex(); - - // Find rows [project on 'shared' field] that has address == memberAddress && removedBy == null - const total = await subscriptionsTable.count(params); - const members = await membersTable.find( - { ...params, limit: params.limit || total }, - // ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'maxMembers', 'streams'], - ['id', 'address', 'shared', 'addedBy', 'removedBy'], - ); - // We assume that member is always on just 1 active subscription - // Find all subscriptions that (subscriptionID == 'shared') - const data = await BluebirdPromise.map( - members, - async member => { - const subscriptions = await subscriptionsTable.find( - { shared: member.shared }, // member[0] - ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'maxMembers', 'streams'], - ); - member.subscription = subscriptions.map( - subscription => ({ subscriptionID: subscription.shared })); - return member; - }, - { concurrency: members.length }, - ); - const result = { - data, + data: {}, meta: { - count: data.length, + count: 0, offset: parseInt(params.offset, 10) || 0, - total, + total: 0, }, }; - return result; + try { + const member = await membersTable.find( + { address: params.memberAddress }, + ['id', 'address', 'shared', 'addedBy', 'removedBy'], + ); + if (member) { + const data = await subscriptionsTable.find( + { subscriptionID: member[0].shared }, + ['subscriptionID', 'creatorAddress', 'price', 'consumable', 'maxMembers', 'streams'], + ); + if (data) { + return { + data, + meta: { + count: 1, + offset: parseInt(params.offset, 10) || 0, + total: 1, + }, + }; + } + } + return result; + } catch (error) { + return { + data: {}, + meta: { + count: 0, + offset: parseInt(params.offset, 10) || 0, + total: 0, + }, + }; + } }; -const getSubscriptions = async (params = {}) => { - if (params.memberAddress !== null) { - return getActiveSubscriptionsForMember(params); - } +const getSubscriptionsBySubscriptionIdOrCreatorAddress = async (params = {}) => { const subscriptionsTable = await getSubscriptionsIndex(); const membersTable = await getMembersIndex(); @@ -95,6 +99,13 @@ const getSubscriptions = async (params = {}) => { return result; }; +const getSubscriptions = async (params = {}) => { + if (params.memberAddress) { + return getActiveSubscriptionsByMemberAddress(params); + } + return getSubscriptionsBySubscriptionIdOrCreatorAddress(params); +}; + module.exports = { getSubscriptions, }; diff --git a/services/gateway/apis/http-version3/methods/subscriptions.js b/services/gateway/apis/http-version3/methods/subscriptions.js index b65960bdd..e8e2ae19f 100644 --- a/services/gateway/apis/http-version3/methods/subscriptions.js +++ b/services/gateway/apis/http-version3/methods/subscriptions.js @@ -27,7 +27,7 @@ module.exports = { params: { creatorAddress: { optional: true, type: 'string', min: 3, max: 41, pattern: regex.ADDRESS_LISK32 }, subscriptionID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, - memberAddress: { type: 'string', required: false }, + memberAddress: { optional: true, type: 'string' }, }, get schema() { const subscriptionSchema = {}; From a8d1cb9fa4ac38ee62e4ed5f5e88507f1728b9f9 Mon Sep 17 00:00:00 2001 From: Ali Haghighatkhah Date: Tue, 4 Jul 2023 20:46:46 +0200 Subject: [PATCH 74/79] Fix concurrency condition --- .../blockchain-indexer/shared/dataService/business/audios.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/blockchain-indexer/shared/dataService/business/audios.js b/services/blockchain-indexer/shared/dataService/business/audios.js index fe067d067..ac7c9e22a 100644 --- a/services/blockchain-indexer/shared/dataService/business/audios.js +++ b/services/blockchain-indexer/shared/dataService/business/audios.js @@ -62,7 +62,7 @@ const getAudios = async (params = {}) => { return audio[0]; }, - { concurrency: audioIDs.length }, + { concurrency: filteredAudioIDs.length }, ); } else { audioData = await audiosTable.find( From c86e774304646e209f9cc7dc3c052ef560429756 Mon Sep 17 00:00:00 2001 From: curvesy Date: Mon, 10 Jul 2023 17:06:01 +0330 Subject: [PATCH 75/79] Remove updateCollection function and use upsert to match the other commands --- .../shared/sdk/constants/eventTopics.js | 2 - .../shared/sdk/constants/names.js | 2 - .../collection/attributeSet.js | 61 ------------------- .../collection/setAttributes.js | 31 +++------- 4 files changed, 8 insertions(+), 88 deletions(-) delete mode 100644 services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js diff --git a/services/blockchain-connector/shared/sdk/constants/eventTopics.js b/services/blockchain-connector/shared/sdk/constants/eventTopics.js index 88fa01383..6a7ecaa48 100644 --- a/services/blockchain-connector/shared/sdk/constants/eventTopics.js +++ b/services/blockchain-connector/shared/sdk/constants/eventTopics.js @@ -80,7 +80,6 @@ const { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_CREATED, - EVENT_NAME_COLLECTION_ATTRIBUTE_SET, EVENT_NAME_COLLECTION_TRANSFERED, MODULE_NAME_AUDIO, @@ -163,7 +162,6 @@ const EVENT_TOPIC_MAPPINGS_BY_MODULE = { }, [MODULE_NAME_COLLECTION]: { [EVENT_NAME_COLLECTION_CREATED]: ['transactionID', 'senderAddress'], - [EVENT_NAME_COLLECTION_ATTRIBUTE_SET]: ['transactionID', 'senderAddress'], [EVENT_NAME_COLLECTION_TRANSFERED]: ['transactionID', 'senderAddress'], }, [MODULE_NAME_AUDIO]: { diff --git a/services/blockchain-connector/shared/sdk/constants/names.js b/services/blockchain-connector/shared/sdk/constants/names.js index 84580fd12..6ae1526ec 100644 --- a/services/blockchain-connector/shared/sdk/constants/names.js +++ b/services/blockchain-connector/shared/sdk/constants/names.js @@ -93,7 +93,6 @@ const EVENT_NAME_SUBSCRIPTION_PURCHASED = 'subscriptionPurchased'; // Collection const MODULE_NAME_COLLECTION = 'collection'; const EVENT_NAME_COLLECTION_CREATED = 'collectionCreated'; -const EVENT_NAME_COLLECTION_ATTRIBUTE_SET = 'collectionAttributeSet'; const EVENT_NAME_COLLECTION_TRANSFERED = 'collectionTransfered'; // Audios @@ -174,7 +173,6 @@ module.exports = { MODULE_NAME_COLLECTION, EVENT_NAME_COLLECTION_CREATED, - EVENT_NAME_COLLECTION_ATTRIBUTE_SET, EVENT_NAME_COLLECTION_TRANSFERED, MODULE_NAME_AUDIO, diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js deleted file mode 100644 index fdd9cf606..000000000 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/attributeSet.js +++ /dev/null @@ -1,61 +0,0 @@ -const { - Logger, - MySQL: { getTableInstance }, -} = require('lisk-service-framework'); - -const config = require('../../../../config'); - -const logger = Logger(); - -const MYSQL_ENDPOINT = config.endpoints.mysql; -const collectionsTableSchema = require('../../../database/schema/collections'); -const { - MODULE_NAME_COLLECTION, - EVENT_NAME_COLLECTION_ATTRIBUTE_SET, -} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); - -const getCollectionsTable = () => getTableInstance( - collectionsTableSchema.tableName, - collectionsTableSchema, - MYSQL_ENDPOINT, -); - -// Command specific constants -const COMMAND_NAME = 'setAttributes'; - -const updateCollection = async (collectionsTable, collectionID, updates, dbTrx) => { - const where = { - collectionID, - }; - - await collectionsTable.update({ where, updates }, dbTrx); - logger.debug(`Updated collection with ID ${collectionID}.`); -}; - -// eslint-disable-next-line no-unused-vars -const applyTransaction = async (blockHeader, tx, events, dbTrx) => { - const collectionsTable = await getCollectionsTable(); - - const { data: eventData = {} } = events.find( - ({ module, name }) => module === MODULE_NAME_COLLECTION - && name === EVENT_NAME_COLLECTION_ATTRIBUTE_SET, - ); - - const { collectionID, ...updates } = { - ...eventData, - ...tx.params, - }; - - await updateCollection(collectionsTable, collectionID, updates, dbTrx); -}; - -// eslint-disable-next-line no-unused-vars -const revertTransaction = async (blockHeader, tx, events, dbTrx) => { - -}; - -module.exports = { - COMMAND_NAME, - applyTransaction, - revertTransaction, -}; diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/setAttributes.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/setAttributes.js index fdd9cf606..c55c74e71 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/setAttributes.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/collection/setAttributes.js @@ -9,10 +9,6 @@ const logger = Logger(); const MYSQL_ENDPOINT = config.endpoints.mysql; const collectionsTableSchema = require('../../../database/schema/collections'); -const { - MODULE_NAME_COLLECTION, - EVENT_NAME_COLLECTION_ATTRIBUTE_SET, -} = require('../../../../../blockchain-connector/shared/sdk/constants/names'); const getCollectionsTable = () => getTableInstance( collectionsTableSchema.tableName, @@ -23,30 +19,19 @@ const getCollectionsTable = () => getTableInstance( // Command specific constants const COMMAND_NAME = 'setAttributes'; -const updateCollection = async (collectionsTable, collectionID, updates, dbTrx) => { - const where = { - collectionID, - }; - - await collectionsTable.update({ where, updates }, dbTrx); - logger.debug(`Updated collection with ID ${collectionID}.`); -}; - // eslint-disable-next-line no-unused-vars const applyTransaction = async (blockHeader, tx, events, dbTrx) => { const collectionsTable = await getCollectionsTable(); - - const { data: eventData = {} } = events.find( - ({ module, name }) => module === MODULE_NAME_COLLECTION - && name === EVENT_NAME_COLLECTION_ATTRIBUTE_SET, + const [collection] = await collectionsTable.find( + { collectionID: tx.params.collectionID }, + ['collectionID'], ); - const { collectionID, ...updates } = { - ...eventData, - ...tx.params, - }; - - await updateCollection(collectionsTable, collectionID, updates, dbTrx); + if (typeof collection !== 'undefined') { + logger.trace(`Update collection with ID ${tx.params.collectionID}.`); + await collectionsTable.upsert(tx.params, dbTrx); + logger.debug(`Updated collection with ID ${tx.params.collectionID}.`); + } }; // eslint-disable-next-line no-unused-vars From 3f5369290548333090e86acbd8c4bb30534298aa Mon Sep 17 00:00:00 2001 From: reyraa Date: Wed, 12 Jul 2023 11:10:30 +0200 Subject: [PATCH 76/79] Add limit and offset to the params --- .../blockchain-indexer/shared/dataService/business/audios.js | 2 +- services/gateway/apis/http-version3/methods/audios.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/services/blockchain-indexer/shared/dataService/business/audios.js b/services/blockchain-indexer/shared/dataService/business/audios.js index ac7c9e22a..07e6c3fd2 100644 --- a/services/blockchain-indexer/shared/dataService/business/audios.js +++ b/services/blockchain-indexer/shared/dataService/business/audios.js @@ -46,7 +46,7 @@ const getAudios = async (params = {}) => { if (params.ownerAddress) { // audiosID const audioIDs = await ownersTable.find( - { address: params.ownerAddress }, + { address: params.ownerAddress, limit: params.limit }, ['audioID', 'shares'], ); diff --git a/services/gateway/apis/http-version3/methods/audios.js b/services/gateway/apis/http-version3/methods/audios.js index 235fae4df..6d2c52b48 100644 --- a/services/gateway/apis/http-version3/methods/audios.js +++ b/services/gateway/apis/http-version3/methods/audios.js @@ -13,6 +13,8 @@ module.exports = { audioID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, collectionID: { optional: true, type: 'string', min: 1, max: 64, pattern: regex.HASH_SHA256 }, ownerAddress: { optional: true, type: 'string', min: 3, max: 41, pattern: regex.ADDRESS_LISK32 }, + limit: { optional: true, type: 'number', min: 1, max: 100, default: 10 }, + offset: { optional: true, type: 'number', min: 0, default: 0 }, }, get schema() { const audioSchema = {}; From 914a8f834e43b88c83adac1cc76455715a0368c8 Mon Sep 17 00:00:00 2001 From: reyraa Date: Wed, 12 Jul 2023 11:11:11 +0200 Subject: [PATCH 77/79] Return all entity parameters/values --- .../blockchain-indexer/shared/dataService/business/profiles.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/services/blockchain-indexer/shared/dataService/business/profiles.js b/services/blockchain-indexer/shared/dataService/business/profiles.js index a8349379d..7b251bbee 100644 --- a/services/blockchain-indexer/shared/dataService/business/profiles.js +++ b/services/blockchain-indexer/shared/dataService/business/profiles.js @@ -25,8 +25,7 @@ const getProfiles = async (params = {}) => { const total = await profilesTable.count(params); const profilesData = await profilesTable.find( { ...params, limit: params.limit || 10 }, - // ['profileID', 'name', 'nickName', 'description', 'socialAccounts', 'creatorAddress'], - ['profileID', 'name', 'nickName', 'description', 'creatorAddress'], + ['profileID', 'name', 'nickName', 'description', 'creatorAddress', 'avatarHash', 'avatarSignature', 'bannerHash', 'bannerSignature'], ); const socialAccountsTable = await getSocialAccountsIndex(); From 76e492021abd961358fef63a41404783204efa77 Mon Sep 17 00:00:00 2001 From: reyraa Date: Wed, 12 Jul 2023 12:46:54 +0200 Subject: [PATCH 78/79] Add offset and limit to the unit test models --- services/gateway/tests/constants/generateDocs.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/services/gateway/tests/constants/generateDocs.js b/services/gateway/tests/constants/generateDocs.js index c65783f41..f2d39bdd9 100644 --- a/services/gateway/tests/constants/generateDocs.js +++ b/services/gateway/tests/constants/generateDocs.js @@ -30,6 +30,12 @@ const createApiDocsExpectedResponse = { { $ref: '#/parameters/ownerAddress', }, + { + $ref: '#/parameters/limit', + }, + { + $ref: '#/parameters/offset', + }, ], responses: { 200: { From 74e40d973224bdb55d087871ee898edb164cc18f Mon Sep 17 00:00:00 2001 From: reyraa Date: Thu, 13 Jul 2023 11:18:17 +0200 Subject: [PATCH 79/79] Fix social media storage --- .../shared/database/schema/socialAccounts.js | 4 ++-- .../shared/indexer/transactionProcessor/profile/create.js | 7 +++---- .../indexer/transactionProcessor/profile/setAttribute.js | 3 +-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/services/blockchain-indexer/shared/database/schema/socialAccounts.js b/services/blockchain-indexer/shared/database/schema/socialAccounts.js index c36801869..c6d884724 100644 --- a/services/blockchain-indexer/shared/database/schema/socialAccounts.js +++ b/services/blockchain-indexer/shared/database/schema/socialAccounts.js @@ -1,10 +1,10 @@ module.exports = { tableName: 'socialAccounts', - primaryKey: 'profileID', + primaryKey: ['profileID', 'platform'], schema: { profileID: { type: 'string' }, username: { type: 'string', null: true, defaultValue: null }, - platform: { type: 'string', null: true, defaultValue: null }, + platform: { type: 'string' }, }, indexes: { profileID: { type: 'string' }, diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js index 00d78dc15..cb4c3cb0c 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/create.js @@ -73,12 +73,11 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { async socialAccount => { const socialInfo = { profileID: eventData.profileID, - username: socialAccount.username, - platform: socialAccount.platform, + ...socialAccount, }; - logger.trace(`Inserting social sccounts for the profile with ID ${eventData.profileID}.`); + logger.trace(`Inserting social accounts for the profile with ID ${eventData.profileID}.`); await socialAccountsTable.upsert(socialInfo, dbTrx); - logger.debug(`Inserted social sccounts for the profile with ID ${eventData.profileID}.`); + logger.debug(`Inserted social accounts for the profile with ID ${eventData.profileID}.`); return true; }, { concurrency: tx.params.socialAccounts.length }, diff --git a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js index b0bb4c9ae..dcfb8b44c 100644 --- a/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js +++ b/services/blockchain-indexer/shared/indexer/transactionProcessor/profile/setAttribute.js @@ -61,8 +61,7 @@ const applyTransaction = async (blockHeader, tx, events, dbTrx) => { async (socialAccount) => { const socialInfo = { profileID: existingProfile.profileID, - username: socialAccount.username, - platform: socialAccount.platform, + ...socialAccount, }; logger.trace(`Updating social accounts for the profile with ID ${existingProfile.profileID}.`); await socialAccountsTable.upsert(socialInfo, dbTrx);