From d1e7b1de5db22911f7aeaf461328a2dbe9c5986a Mon Sep 17 00:00:00 2001 From: owl352 Date: Fri, 1 Nov 2024 01:21:50 +0300 Subject: [PATCH 01/13] work work work --- packages/api/.env | 1 + packages/api/src/DAPI.js | 41 +++++++-- packages/api/src/constants.js | 1 + .../src/controllers/IdentitiesController.js | 91 ++++++++++++++----- .../api/src/controllers/MainController.js | 63 +++++++++---- packages/api/src/models/Identity.js | 4 + packages/api/src/utils.js | 62 ++++++++++++- 7 files changed, 212 insertions(+), 51 deletions(-) diff --git a/packages/api/.env b/packages/api/.env index f784f739d..414941241 100644 --- a/packages/api/.env +++ b/packages/api/.env @@ -10,3 +10,4 @@ DASHCORE_PASS=password EPOCH_CHANGE_TIME=3600000 DAPI_URL=127.0.0.1:1443:self-signed TCP_CONNECT_TIMEOUT=400 +DPNS_CONTRACT=5mjGWa9mruHnLBht3ntbfgodcSoJxA1XIfYiv1PFMVU= diff --git a/packages/api/src/DAPI.js b/packages/api/src/DAPI.js index a2fc9ba64..955072d17 100644 --- a/packages/api/src/DAPI.js +++ b/packages/api/src/DAPI.js @@ -1,28 +1,53 @@ -const { Identifier } = require('dash').PlatformProtocol +const {Identifier} = require('dash').PlatformProtocol + +const { + v0: {GetDocumentsRequest}, +} = require('@dashevo/dapi-grpc'); class DAPI { dapi dpp - constructor (dapi, dpp) { + constructor(dapi, dpp) { this.dapi = dapi this.dpp = dpp } - async getIdentityBalance (identifier) { - const { balance } = await this.dapi.platform.getIdentityBalance(Identifier.from(identifier)) + async getIdentityBalance(identifier) { + const {balance} = await this.dapi.platform.getIdentityBalance(Identifier.from(identifier)) return balance } - async getTotalCredits () { - const { totalCreditsInPlatform } = await this.dapi.platform.getTotalCreditsInPlatform() + async getTotalCredits() { + const {totalCreditsInPlatform} = await this.dapi.platform.getTotalCreditsInPlatform() return totalCreditsInPlatform } - async getEpochsInfo (count, start, ascending) { - const { epochsInfo } = await this.dapi.platform.getEpochsInfo(start, count, { ascending }) + async getEpochsInfo(count, start, ascending) { + const {epochsInfo} = await this.dapi.platform.getEpochsInfo(start, count, {ascending}) return epochsInfo } + + async getContestedState(contractId, + documentTypeName, + indexName = 'parentNameAndLabel', + resultType = 2, + indexValuesList, startAtIdentifierInfo, + allowIncludeLockedAndAbstainingVoteTally, + count + ) { + const {contestedResourceContenders} = await this.dapi.platform.getContestedResourceVoteState( + Buffer.from(contractId, 'base64'), + documentTypeName, + indexName, + resultType, + indexValuesList, + startAtIdentifierInfo, + allowIncludeLockedAndAbstainingVoteTally, + count + ) + return contestedResourceContenders + } } module.exports = DAPI diff --git a/packages/api/src/constants.js b/packages/api/src/constants.js index 729ac0a23..72f606188 100644 --- a/packages/api/src/constants.js +++ b/packages/api/src/constants.js @@ -5,6 +5,7 @@ let genesisTime module.exports = { EPOCH_CHANGE_TIME: Number(process.env.EPOCH_CHANGE_TIME), TCP_CONNECT_TIMEOUT: Number(process.env.TCP_CONNECT_TIMEOUT), + DPNS_CONTRACT: process.env.DPNS_CONTRACT ?? '5mjGWa9mruHnLBht3ntbfgodcSoJxA1XIfYiv1PFMVU=', get genesisTime () { if (!genesisTime || isNaN(genesisTime)) { return TenderdashRPC.getBlockByHeight(1).then((blockInfo) => { diff --git a/packages/api/src/controllers/IdentitiesController.js b/packages/api/src/controllers/IdentitiesController.js index 0e6092fe1..8f41cc4f9 100644 --- a/packages/api/src/controllers/IdentitiesController.js +++ b/packages/api/src/controllers/IdentitiesController.js @@ -1,56 +1,99 @@ const IdentitiesDAO = require('../dao/IdentitiesDAO') -const { IDENTITY_CREDIT_WITHDRAWAL } = require('../enums/StateTransitionEnum') +const {IDENTITY_CREDIT_WITHDRAWAL} = require('../enums/StateTransitionEnum') +const {generateNormalizedLabelBuffer, getLabelBuffer, validateAliases} = require("../utils"); +const Identity = require("../models/Identity"); +const {base58} = require("@scure/base"); class IdentitiesController { - constructor (knex, dapi) { + constructor(knex, dapi) { this.identitiesDAO = new IdentitiesDAO(knex) this.dapi = dapi } getIdentityByIdentifier = async (request, response) => { - const { identifier } = request.params - - const identity = await this.identitiesDAO.getIdentityByIdentifier(identifier) + const {identifier} = request.params + let [identity] = await Promise.all([this.identitiesDAO.getIdentityByIdentifier(identifier)]) if (!identity) { - return response.status(404).send({ message: 'not found' }) + return response.status(404).send({message: 'not found'}) } + const validatedAliases = await validateAliases(identity.aliases, identity.identifier, this.dapi) + + identity = Identity.fromObject({...identity, aliases: validatedAliases}) + const balance = await this.dapi.getIdentityBalance(identifier) - response.send({ ...identity, balance }) + response.send({...identity, balance}) } + getIdentityByDPNS = async (request, response) => { - const { dpns } = request.query + const {dpns} = request.query + + let preIdentity + let identity - const identity = await this.identitiesDAO.getIdentityByDPNS(dpns) + let contestedState + + if (!dpns.includes('.')) { + preIdentity = await this.identitiesDAO.getIdentityByDPNS(dpns) + + if (!preIdentity) { + return response.status(404).send({message: 'not found'}) + } + } + + [{contestedState}] = await validateAliases( + [preIdentity ? preIdentity.aliases.find(v => v.includes(dpns)) : dpns], + null, + this.dapi + ) + + if (contestedState) { + if (typeof contestedState.finishedVoteInfo?.wonByIdentityId === 'string') { + const identifier = base58.encode(Buffer.from(contestedState.finishedVoteInfo?.wonByIdentityId, 'base64')) + + identity = await this.identitiesDAO.getIdentityByIdentifier(identifier) + } + } + + if (!contestedState) { + identity = preIdentity + } if (!identity) { - return response.status(404).send({ message: 'not found' }) + return response.status(404).send({message: 'not found'}) } const balance = await this.dapi.getIdentityBalance(identity.identifier) - response.send({ ...identity, balance }) + const validatedAliases = await validateAliases(identity.aliases, identity.identifier, this.dapi) + + identity = Identity.fromObject({...identity, aliases: validatedAliases}) + + response.send({...identity, balance}) } getIdentities = async (request, response) => { - const { page = 1, limit = 10, order = 'asc', order_by: orderBy = 'block_height' } = request.query + const {page = 1, limit = 10, order = 'asc', order_by: orderBy = 'block_height'} = request.query const identities = await this.identitiesDAO.getIdentities(Number(page ?? 1), Number(limit ?? 10), order, orderBy) const identitiesWithBalance = await Promise.all(identities.resultSet.map(async identity => { const balance = await this.dapi.getIdentityBalance(identity.identifier) - return { ...identity, balance } + + const validatedAliases = await validateAliases(identity.aliases, identity.identifier, this.dapi) + + return {...identity, aliases: validatedAliases, balance} })) - response.send({ ...identities, resultSet: identitiesWithBalance }) + response.send({...identities, resultSet: identitiesWithBalance}) } getTransactionsByIdentity = async (request, response) => { - const { identifier } = request.params - const { page = 1, limit = 10, order = 'asc' } = request.query + const {identifier} = request.params + const {page = 1, limit = 10, order = 'asc'} = request.query const transactions = await this.identitiesDAO.getTransactionsByIdentity(identifier, Number(page ?? 1), Number(limit ?? 10), order) @@ -58,8 +101,8 @@ class IdentitiesController { } getDataContractsByIdentity = async (request, response) => { - const { identifier } = request.params - const { page = 1, limit = 10, order = 'asc' } = request.query + const {identifier} = request.params + const {page = 1, limit = 10, order = 'asc'} = request.query const dataContracts = await this.identitiesDAO.getDataContractsByIdentity(identifier, Number(page ?? 1), Number(limit ?? 10), order) @@ -67,8 +110,8 @@ class IdentitiesController { } getDocumentsByIdentity = async (request, response) => { - const { identifier } = request.params - const { page = 1, limit = 10, order = 'asc' } = request.query + const {identifier} = request.params + const {page = 1, limit = 10, order = 'asc'} = request.query const documents = await this.identitiesDAO.getDocumentsByIdentity(identifier, Number(page ?? 1), Number(limit ?? 10), order) @@ -76,8 +119,8 @@ class IdentitiesController { } getTransfersByIdentity = async (request, response) => { - const { identifier } = request.params - const { page = 1, limit = 10, order = 'asc', type = undefined } = request.query + const {identifier} = request.params + const {page = 1, limit = 10, order = 'asc', type = undefined} = request.query const transfers = await this.identitiesDAO.getTransfersByIdentity(identifier, Number(page ?? 1), Number(limit ?? 10), order, type) @@ -85,8 +128,8 @@ class IdentitiesController { } getWithdrawalsByIdentity = async (request, response) => { - const { identifier } = request.params - const { page = 1, limit = 10, order = 'asc' } = request.query + const {identifier} = request.params + const {page = 1, limit = 10, order = 'asc'} = request.query const withdrawals = await this.identitiesDAO.getTransfersByIdentity( identifier, diff --git a/packages/api/src/controllers/MainController.js b/packages/api/src/controllers/MainController.js index bbe3b6590..7a7aa28a6 100644 --- a/packages/api/src/controllers/MainController.js +++ b/packages/api/src/controllers/MainController.js @@ -6,12 +6,14 @@ const IdentitiesDAO = require('../dao/IdentitiesDAO') const ValidatorsDAO = require('../dao/ValidatorsDAO') const TenderdashRPC = require('../tenderdashRpc') const Epoch = require('../models/Epoch') +const {validateAliases} = require("../utils"); +const {base58} = require("@scure/base"); const API_VERSION = require('../../package.json').version const PLATFORM_VERSION = '1' + require('../../package.json').dependencies.dash.substring(1) class MainController { - constructor (knex, dapi) { + constructor(knex, dapi) { this.blocksDAO = new BlocksDAO(knex) this.dataContractsDAO = new DataContractsDAO(knex) this.documentsDAO = new DocumentsDAO(knex) @@ -70,7 +72,7 @@ class MainController { } search = async (request, response) => { - const { query } = request.query + const {query} = request.query const epoch = Epoch.fromObject({ startTime: 0, @@ -82,7 +84,7 @@ class MainController { const block = await this.blocksDAO.getBlockByHeight(query) if (block) { - return response.send({ block }) + return response.send({block}) } } @@ -91,21 +93,21 @@ class MainController { const block = await this.blocksDAO.getBlockByHash(query) if (block) { - return response.send({ block }) + return response.send({block}) } // search transactions const transaction = await this.transactionsDAO.getTransactionByHash(query) if (transaction) { - return response.send({ transaction }) + return response.send({transaction}) } // search validators const validator = await this.validatorsDAO.getValidatorByProTxHash(query, null, epoch) if (validator) { - return response.send({ validator }) + return response.send({validator}) } } @@ -115,37 +117,66 @@ class MainController { const identity = await this.identitiesDAO.getIdentityByIdentifier(query) if (identity) { - const balance = await this.dapi.getIdentityBalance(identity.identifier) - - return response.send({ identity: { ...identity, balance } }) + // Sending without actual balance and aliases, because on frontend we were making + // request /identity/:identifier for actual data + return response.send({identity}) } // search data contracts const dataContract = await this.dataContractsDAO.getDataContractByIdentifier(query) if (dataContract) { - return response.send({ dataContract }) + return response.send({dataContract}) } // search documents const document = await this.documentsDAO.getDocumentByIdentifier(query) if (document) { - return response.send({ document }) + return response.send({document}) } } if (/^[^\s.]+(\.[^\s.]+)*$/.test(query)) { - const identity = await this.identitiesDAO.getIdentityByDPNS(query) + let preIdentity + let identity - if (identity) { - const balance = await this.dapi.getIdentityBalance(identity.identifier) + let contestedState + + if (!query.includes('.')) { + preIdentity = await this.identitiesDAO.getIdentityByDPNS(query) + + if (!preIdentity) { + return response.status(404).send({message: 'not found'}) + } + } + + [{contestedState}] = await validateAliases( + [preIdentity ? preIdentity.aliases.find(v => v.includes(query)) : query], + null, + this.dapi + ) - return response.send({ identity: { ...identity, balance } }) + if (contestedState) { + if (typeof contestedState.finishedVoteInfo?.wonByIdentityId === 'string') { + const identifier = base58.encode(Buffer.from(contestedState.finishedVoteInfo?.wonByIdentityId, 'base64')) + + identity = await this.identitiesDAO.getIdentityByIdentifier(identifier) + } + } + + if (!contestedState) { + identity = preIdentity + } + + if (identity) { + // Sending without actual balance and aliases, because on frontend we were making + // request /identity/:identifier for actual data + return response.send({identity}) } } - response.status(404).send({ message: 'not found' }) + response.status(404).send({message: 'not found'}) } } diff --git a/packages/api/src/models/Identity.js b/packages/api/src/models/Identity.js index ddfd01e6d..1456ca6ee 100644 --- a/packages/api/src/models/Identity.js +++ b/packages/api/src/models/Identity.js @@ -30,4 +30,8 @@ module.exports = class Identity { static fromRow ({ identifier, owner, revision, balance, timestamp, total_txs, total_data_contracts, total_documents, total_transfers, tx_hash, is_system, aliases }) { return new Identity(identifier, owner, revision, Number(balance), timestamp, Number(total_txs), Number(total_data_contracts), Number(total_documents), Number(total_transfers), tx_hash, is_system, aliases) } + + static fromObject({identifier, owner, revision, balance, timestamp, totalTxs, totalDataContracts, totalDocuments, totalTransfers, txHash, isSystem, aliases}){ + return new Identity(identifier, owner, revision, balance, timestamp, totalTxs, totalDataContracts, totalDocuments, totalTransfers, txHash, isSystem, aliases) + } } diff --git a/packages/api/src/utils.js b/packages/api/src/utils.js index f07c5a48c..69a928fc2 100644 --- a/packages/api/src/utils.js +++ b/packages/api/src/utils.js @@ -1,7 +1,8 @@ const crypto = require('crypto') const StateTransitionEnum = require('./enums/StateTransitionEnum') const net = require('net') -const { TCP_CONNECT_TIMEOUT } = require('./constants') +const {TCP_CONNECT_TIMEOUT, DPNS_CONTRACT} = require('./constants') +const {base58} = require("@scure/base"); const getKnex = () => { return require('knex')({ @@ -12,7 +13,7 @@ const getKnex = () => { user: process.env.POSTGRES_USER, database: process.env.POSTGRES_DB, password: process.env.POSTGRES_PASS, - ssl: process.env.POSTGRES_SSL ? { rejectUnauthorized: false } : false + ssl: process.env.POSTGRES_SSL ? {rejectUnauthorized: false} : false } }) } @@ -174,4 +175,59 @@ const calculateInterval = (start, end) => { }, intervalsInRFC[0]) } -module.exports = { hash, decodeStateTransition, getKnex, checkTcpConnect, calculateInterval } +const getLabelBuffer = (text) => + Buffer.from(`${ + Buffer.from(`12${`0${(text.length).toString(16)}`.slice(-2) + }`, 'hex')}${text}` + ) + +const validateAliases = async (aliases, identifier, dapi) => { + const aliasesWithContestedState = await Promise.all(aliases.map(async (alias) => { + const [label, domain] = alias.split('.') + + const normalizedLabel = label.toLowerCase().replace(/[oli]/g, (match) => { + if (match === 'o') { + return '0'; + } + if (match === 'l' || match === 'i') { + return '1'; + } + return match; + }) + + if (/^[a-zA-Z01]{3,19}$/.test(normalizedLabel)) { + + const domainBuffer = getLabelBuffer(domain) + const labelBuffer = getLabelBuffer(normalizedLabel) + + const contestedState = await dapi.getContestedState( + DPNS_CONTRACT, + 'domain', + 'parentNameAndLabel', + 1, + [ + domainBuffer, + labelBuffer + ] + ) + + return {alias, contestedState} + } + + return {alias, contestedState: null} + })) + + return (identifier ? aliasesWithContestedState.filter(alias => ( + typeof alias.contestedState?.finishedVoteInfo?.wonByIdentityId === 'string' + ? base58.encode(Buffer.from(alias.contestedState?.finishedVoteInfo.wonByIdentityId, 'base64')) === identifier + : false + ) || alias.contestedState === null + ).map(v => v.alias) + : aliasesWithContestedState) +} + +module.exports = { + hash, decodeStateTransition, + getKnex, checkTcpConnect, calculateInterval, + validateAliases, getLabelBuffer +} From 8b8009680f3ca45794e053a3950adf65790c43ec Mon Sep 17 00:00:00 2001 From: owl352 Date: Fri, 1 Nov 2024 01:40:27 +0300 Subject: [PATCH 02/13] lint and fixes --- packages/api/src/DAPI.js | 36 +++++------ .../src/controllers/IdentitiesController.js | 63 +++++++++---------- .../api/src/controllers/MainController.js | 36 +++++------ packages/api/src/models/Identity.js | 2 +- packages/api/src/utils.js | 38 ++++++----- 5 files changed, 85 insertions(+), 90 deletions(-) diff --git a/packages/api/src/DAPI.js b/packages/api/src/DAPI.js index 955072d17..9f5d02bd9 100644 --- a/packages/api/src/DAPI.js +++ b/packages/api/src/DAPI.js @@ -1,42 +1,38 @@ -const {Identifier} = require('dash').PlatformProtocol - -const { - v0: {GetDocumentsRequest}, -} = require('@dashevo/dapi-grpc'); +const { Identifier } = require('dash').PlatformProtocol class DAPI { dapi dpp - constructor(dapi, dpp) { + constructor (dapi, dpp) { this.dapi = dapi this.dpp = dpp } - async getIdentityBalance(identifier) { - const {balance} = await this.dapi.platform.getIdentityBalance(Identifier.from(identifier)) + async getIdentityBalance (identifier) { + const { balance } = await this.dapi.platform.getIdentityBalance(Identifier.from(identifier)) return balance } - async getTotalCredits() { - const {totalCreditsInPlatform} = await this.dapi.platform.getTotalCreditsInPlatform() + async getTotalCredits () { + const { totalCreditsInPlatform } = await this.dapi.platform.getTotalCreditsInPlatform() return totalCreditsInPlatform } - async getEpochsInfo(count, start, ascending) { - const {epochsInfo} = await this.dapi.platform.getEpochsInfo(start, count, {ascending}) + async getEpochsInfo (count, start, ascending) { + const { epochsInfo } = await this.dapi.platform.getEpochsInfo(start, count, { ascending }) return epochsInfo } - async getContestedState(contractId, - documentTypeName, - indexName = 'parentNameAndLabel', - resultType = 2, - indexValuesList, startAtIdentifierInfo, - allowIncludeLockedAndAbstainingVoteTally, - count + async getContestedState (contractId, + documentTypeName, + indexName = 'parentNameAndLabel', + resultType = 2, + indexValuesList, startAtIdentifierInfo, + allowIncludeLockedAndAbstainingVoteTally, + count ) { - const {contestedResourceContenders} = await this.dapi.platform.getContestedResourceVoteState( + const { contestedResourceContenders } = await this.dapi.platform.getContestedResourceVoteState( Buffer.from(contractId, 'base64'), documentTypeName, indexName, diff --git a/packages/api/src/controllers/IdentitiesController.js b/packages/api/src/controllers/IdentitiesController.js index 8f41cc4f9..7c1f81e61 100644 --- a/packages/api/src/controllers/IdentitiesController.js +++ b/packages/api/src/controllers/IdentitiesController.js @@ -1,51 +1,48 @@ const IdentitiesDAO = require('../dao/IdentitiesDAO') -const {IDENTITY_CREDIT_WITHDRAWAL} = require('../enums/StateTransitionEnum') -const {generateNormalizedLabelBuffer, getLabelBuffer, validateAliases} = require("../utils"); -const Identity = require("../models/Identity"); -const {base58} = require("@scure/base"); +const { IDENTITY_CREDIT_WITHDRAWAL } = require('../enums/StateTransitionEnum') +const { validateAliases } = require('../utils') +const Identity = require('../models/Identity') +const { base58 } = require('@scure/base') class IdentitiesController { - constructor(knex, dapi) { + constructor (knex, dapi) { this.identitiesDAO = new IdentitiesDAO(knex) this.dapi = dapi } getIdentityByIdentifier = async (request, response) => { - const {identifier} = request.params + const { identifier } = request.params let [identity] = await Promise.all([this.identitiesDAO.getIdentityByIdentifier(identifier)]) if (!identity) { - return response.status(404).send({message: 'not found'}) + return response.status(404).send({ message: 'not found' }) } const validatedAliases = await validateAliases(identity.aliases, identity.identifier, this.dapi) - identity = Identity.fromObject({...identity, aliases: validatedAliases}) + identity = Identity.fromObject({ ...identity, aliases: validatedAliases }) const balance = await this.dapi.getIdentityBalance(identifier) - response.send({...identity, balance}) + response.send({ ...identity, balance }) } - getIdentityByDPNS = async (request, response) => { - const {dpns} = request.query + const { dpns } = request.query let preIdentity let identity - let contestedState - if (!dpns.includes('.')) { preIdentity = await this.identitiesDAO.getIdentityByDPNS(dpns) if (!preIdentity) { - return response.status(404).send({message: 'not found'}) + return response.status(404).send({ message: 'not found' }) } } - [{contestedState}] = await validateAliases( - [preIdentity ? preIdentity.aliases.find(v => v.includes(dpns)) : dpns], + const [{ contestedState }] = await validateAliases( + [preIdentity ? preIdentity.aliases.find(v => v.includes(`${dpns}.`)) : dpns], null, this.dapi ) @@ -59,24 +56,24 @@ class IdentitiesController { } if (!contestedState) { - identity = preIdentity + identity = preIdentity ?? await this.identitiesDAO.getIdentityByDPNS(dpns) } if (!identity) { - return response.status(404).send({message: 'not found'}) + return response.status(404).send({ message: 'not found' }) } const balance = await this.dapi.getIdentityBalance(identity.identifier) const validatedAliases = await validateAliases(identity.aliases, identity.identifier, this.dapi) - identity = Identity.fromObject({...identity, aliases: validatedAliases}) + identity = Identity.fromObject({ ...identity, aliases: validatedAliases }) - response.send({...identity, balance}) + response.send({ ...identity, balance }) } getIdentities = async (request, response) => { - const {page = 1, limit = 10, order = 'asc', order_by: orderBy = 'block_height'} = request.query + const { page = 1, limit = 10, order = 'asc', order_by: orderBy = 'block_height' } = request.query const identities = await this.identitiesDAO.getIdentities(Number(page ?? 1), Number(limit ?? 10), order, orderBy) @@ -85,15 +82,15 @@ class IdentitiesController { const validatedAliases = await validateAliases(identity.aliases, identity.identifier, this.dapi) - return {...identity, aliases: validatedAliases, balance} + return { ...identity, aliases: validatedAliases, balance } })) - response.send({...identities, resultSet: identitiesWithBalance}) + response.send({ ...identities, resultSet: identitiesWithBalance }) } getTransactionsByIdentity = async (request, response) => { - const {identifier} = request.params - const {page = 1, limit = 10, order = 'asc'} = request.query + const { identifier } = request.params + const { page = 1, limit = 10, order = 'asc' } = request.query const transactions = await this.identitiesDAO.getTransactionsByIdentity(identifier, Number(page ?? 1), Number(limit ?? 10), order) @@ -101,8 +98,8 @@ class IdentitiesController { } getDataContractsByIdentity = async (request, response) => { - const {identifier} = request.params - const {page = 1, limit = 10, order = 'asc'} = request.query + const { identifier } = request.params + const { page = 1, limit = 10, order = 'asc' } = request.query const dataContracts = await this.identitiesDAO.getDataContractsByIdentity(identifier, Number(page ?? 1), Number(limit ?? 10), order) @@ -110,8 +107,8 @@ class IdentitiesController { } getDocumentsByIdentity = async (request, response) => { - const {identifier} = request.params - const {page = 1, limit = 10, order = 'asc'} = request.query + const { identifier } = request.params + const { page = 1, limit = 10, order = 'asc' } = request.query const documents = await this.identitiesDAO.getDocumentsByIdentity(identifier, Number(page ?? 1), Number(limit ?? 10), order) @@ -119,8 +116,8 @@ class IdentitiesController { } getTransfersByIdentity = async (request, response) => { - const {identifier} = request.params - const {page = 1, limit = 10, order = 'asc', type = undefined} = request.query + const { identifier } = request.params + const { page = 1, limit = 10, order = 'asc', type = undefined } = request.query const transfers = await this.identitiesDAO.getTransfersByIdentity(identifier, Number(page ?? 1), Number(limit ?? 10), order, type) @@ -128,8 +125,8 @@ class IdentitiesController { } getWithdrawalsByIdentity = async (request, response) => { - const {identifier} = request.params - const {page = 1, limit = 10, order = 'asc'} = request.query + const { identifier } = request.params + const { page = 1, limit = 10, order = 'asc' } = request.query const withdrawals = await this.identitiesDAO.getTransfersByIdentity( identifier, diff --git a/packages/api/src/controllers/MainController.js b/packages/api/src/controllers/MainController.js index 7a7aa28a6..f98d77e59 100644 --- a/packages/api/src/controllers/MainController.js +++ b/packages/api/src/controllers/MainController.js @@ -6,14 +6,14 @@ const IdentitiesDAO = require('../dao/IdentitiesDAO') const ValidatorsDAO = require('../dao/ValidatorsDAO') const TenderdashRPC = require('../tenderdashRpc') const Epoch = require('../models/Epoch') -const {validateAliases} = require("../utils"); -const {base58} = require("@scure/base"); +const { validateAliases } = require('../utils') +const { base58 } = require('@scure/base') const API_VERSION = require('../../package.json').version const PLATFORM_VERSION = '1' + require('../../package.json').dependencies.dash.substring(1) class MainController { - constructor(knex, dapi) { + constructor (knex, dapi) { this.blocksDAO = new BlocksDAO(knex) this.dataContractsDAO = new DataContractsDAO(knex) this.documentsDAO = new DocumentsDAO(knex) @@ -72,7 +72,7 @@ class MainController { } search = async (request, response) => { - const {query} = request.query + const { query } = request.query const epoch = Epoch.fromObject({ startTime: 0, @@ -84,7 +84,7 @@ class MainController { const block = await this.blocksDAO.getBlockByHeight(query) if (block) { - return response.send({block}) + return response.send({ block }) } } @@ -93,21 +93,21 @@ class MainController { const block = await this.blocksDAO.getBlockByHash(query) if (block) { - return response.send({block}) + return response.send({ block }) } // search transactions const transaction = await this.transactionsDAO.getTransactionByHash(query) if (transaction) { - return response.send({transaction}) + return response.send({ transaction }) } // search validators const validator = await this.validatorsDAO.getValidatorByProTxHash(query, null, epoch) if (validator) { - return response.send({validator}) + return response.send({ validator }) } } @@ -119,21 +119,21 @@ class MainController { if (identity) { // Sending without actual balance and aliases, because on frontend we were making // request /identity/:identifier for actual data - return response.send({identity}) + return response.send({ identity }) } // search data contracts const dataContract = await this.dataContractsDAO.getDataContractByIdentifier(query) if (dataContract) { - return response.send({dataContract}) + return response.send({ dataContract }) } // search documents const document = await this.documentsDAO.getDocumentByIdentifier(query) if (document) { - return response.send({document}) + return response.send({ document }) } } @@ -141,18 +141,16 @@ class MainController { let preIdentity let identity - let contestedState - if (!query.includes('.')) { preIdentity = await this.identitiesDAO.getIdentityByDPNS(query) if (!preIdentity) { - return response.status(404).send({message: 'not found'}) + return response.status(404).send({ message: 'not found' }) } } - [{contestedState}] = await validateAliases( - [preIdentity ? preIdentity.aliases.find(v => v.includes(query)) : query], + const [{ contestedState }] = await validateAliases( + [preIdentity ? preIdentity.aliases.find(v => v.includes(`${query}.`)) : query], null, this.dapi ) @@ -166,17 +164,17 @@ class MainController { } if (!contestedState) { - identity = preIdentity + identity = preIdentity ?? await this.identitiesDAO.getIdentityByDPNS(query) } if (identity) { // Sending without actual balance and aliases, because on frontend we were making // request /identity/:identifier for actual data - return response.send({identity}) + return response.send({ identity }) } } - response.status(404).send({message: 'not found'}) + response.status(404).send({ message: 'not found' }) } } diff --git a/packages/api/src/models/Identity.js b/packages/api/src/models/Identity.js index 1456ca6ee..229fc2fb3 100644 --- a/packages/api/src/models/Identity.js +++ b/packages/api/src/models/Identity.js @@ -31,7 +31,7 @@ module.exports = class Identity { return new Identity(identifier, owner, revision, Number(balance), timestamp, Number(total_txs), Number(total_data_contracts), Number(total_documents), Number(total_transfers), tx_hash, is_system, aliases) } - static fromObject({identifier, owner, revision, balance, timestamp, totalTxs, totalDataContracts, totalDocuments, totalTransfers, txHash, isSystem, aliases}){ + static fromObject ({ identifier, owner, revision, balance, timestamp, totalTxs, totalDataContracts, totalDocuments, totalTransfers, txHash, isSystem, aliases }) { return new Identity(identifier, owner, revision, balance, timestamp, totalTxs, totalDataContracts, totalDocuments, totalTransfers, txHash, isSystem, aliases) } } diff --git a/packages/api/src/utils.js b/packages/api/src/utils.js index 69a928fc2..41df8978e 100644 --- a/packages/api/src/utils.js +++ b/packages/api/src/utils.js @@ -1,8 +1,8 @@ const crypto = require('crypto') const StateTransitionEnum = require('./enums/StateTransitionEnum') const net = require('net') -const {TCP_CONNECT_TIMEOUT, DPNS_CONTRACT} = require('./constants') -const {base58} = require("@scure/base"); +const { TCP_CONNECT_TIMEOUT, DPNS_CONTRACT } = require('./constants') +const { base58 } = require('@scure/base') const getKnex = () => { return require('knex')({ @@ -13,7 +13,7 @@ const getKnex = () => { user: process.env.POSTGRES_USER, database: process.env.POSTGRES_DB, password: process.env.POSTGRES_PASS, - ssl: process.env.POSTGRES_SSL ? {rejectUnauthorized: false} : false + ssl: process.env.POSTGRES_SSL ? { rejectUnauthorized: false } : false } }) } @@ -187,16 +187,15 @@ const validateAliases = async (aliases, identifier, dapi) => { const normalizedLabel = label.toLowerCase().replace(/[oli]/g, (match) => { if (match === 'o') { - return '0'; + return '0' } if (match === 'l' || match === 'i') { - return '1'; + return '1' } - return match; + return match }) if (/^[a-zA-Z01]{3,19}$/.test(normalizedLabel)) { - const domainBuffer = getLabelBuffer(domain) const labelBuffer = getLabelBuffer(normalizedLabel) @@ -211,23 +210,28 @@ const validateAliases = async (aliases, identifier, dapi) => { ] ) - return {alias, contestedState} + return { alias, contestedState } } - return {alias, contestedState: null} + return { alias, contestedState: null } })) - return (identifier ? aliasesWithContestedState.filter(alias => ( - typeof alias.contestedState?.finishedVoteInfo?.wonByIdentityId === 'string' - ? base58.encode(Buffer.from(alias.contestedState?.finishedVoteInfo.wonByIdentityId, 'base64')) === identifier - : false - ) || alias.contestedState === null + return (identifier + ? aliasesWithContestedState.filter(alias => ( + typeof alias.contestedState?.finishedVoteInfo?.wonByIdentityId === 'string' + ? base58.encode(Buffer.from(alias.contestedState?.finishedVoteInfo.wonByIdentityId, 'base64')) === identifier + : false + ) || alias.contestedState === null ).map(v => v.alias) : aliasesWithContestedState) } module.exports = { - hash, decodeStateTransition, - getKnex, checkTcpConnect, calculateInterval, - validateAliases, getLabelBuffer + hash, + decodeStateTransition, + getKnex, + checkTcpConnect, + calculateInterval, + validateAliases, + getLabelBuffer } From a2ce9ea98d9e15d5d822fad85da2dc0be9fd8739 Mon Sep 17 00:00:00 2001 From: owl352 Date: Fri, 1 Nov 2024 16:09:44 +0300 Subject: [PATCH 03/13] update package.json --- packages/api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/package.json b/packages/api/package.json index 67832c267..39de5ea35 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -12,7 +12,7 @@ "lint": "standard ." }, "dependencies": { - "@dashevo/dapi-client": "1.4.1", + "@dashevo/dapi-client": "github:owl352/dapi-client", "@dashevo/dashd-rpc": "19.0.0", "@fastify/cors": "^8.3.0", "@scure/base": "^1.1.5", From f00f3b58e5c05becae6335f03572927e02f7a862 Mon Sep 17 00:00:00 2001 From: owl352 Date: Fri, 1 Nov 2024 16:41:34 +0300 Subject: [PATCH 04/13] main controller tests update --- packages/api/test/integration/main.spec.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/api/test/integration/main.spec.js b/packages/api/test/integration/main.spec.js index 6600ffeba..8ea73bcab 100644 --- a/packages/api/test/integration/main.spec.js +++ b/packages/api/test/integration/main.spec.js @@ -39,6 +39,8 @@ describe('Other routes', () => { nextEpoch: 0 }]) + mock.method(DAPI.prototype, 'getContestedState', async () => null) + mock.method(tenderdashRpc, 'getBlockByHeight', async () => ({ block: { header: { @@ -228,7 +230,7 @@ describe('Other routes', () => { const expectedIdentity = { identifier: identity.identifier, revision: 0, - balance: 0, + balance: null, timestamp: block.timestamp.toISOString(), txHash: identityTransaction.hash, totalTxs: 51, @@ -251,7 +253,7 @@ describe('Other routes', () => { const expectedIdentity = { identifier: identity.identifier, revision: 0, - balance: 0, + balance: null, timestamp: block.timestamp.toISOString(), txHash: identityTransaction.hash, totalTxs: 51, From 8c6d0927412921f8e212c21202892cc5387c96c6 Mon Sep 17 00:00:00 2001 From: owl352 Date: Fri, 1 Nov 2024 16:54:06 +0300 Subject: [PATCH 05/13] fix --- packages/api/src/controllers/IdentitiesController.js | 7 +++---- packages/api/test/integration/identities.spec.js | 4 +++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/api/src/controllers/IdentitiesController.js b/packages/api/src/controllers/IdentitiesController.js index 7c1f81e61..33cc6ae63 100644 --- a/packages/api/src/controllers/IdentitiesController.js +++ b/packages/api/src/controllers/IdentitiesController.js @@ -12,7 +12,8 @@ class IdentitiesController { getIdentityByIdentifier = async (request, response) => { const { identifier } = request.params - let [identity] = await Promise.all([this.identitiesDAO.getIdentityByIdentifier(identifier)]) + + const identity = await this.identitiesDAO.getIdentityByIdentifier(identifier) if (!identity) { return response.status(404).send({ message: 'not found' }) @@ -20,11 +21,9 @@ class IdentitiesController { const validatedAliases = await validateAliases(identity.aliases, identity.identifier, this.dapi) - identity = Identity.fromObject({ ...identity, aliases: validatedAliases }) - const balance = await this.dapi.getIdentityBalance(identifier) - response.send({ ...identity, balance }) + response.send(Identity.fromObject({ ...identity, aliases: validatedAliases, balance })) } getIdentityByDPNS = async (request, response) => { diff --git a/packages/api/test/integration/identities.spec.js b/packages/api/test/integration/identities.spec.js index abbb4c1e1..133080c51 100644 --- a/packages/api/test/integration/identities.spec.js +++ b/packages/api/test/integration/identities.spec.js @@ -30,6 +30,8 @@ describe('Identities routes', () => { before(async () => { mock.method(DAPI.prototype, 'getIdentityBalance', async () => 0) + mock.method(DAPI.prototype, 'getContestedState', async () => null) + mock.method(tenderdashRpc, 'getBlockByHeight', async () => ({ block: { header: { @@ -58,7 +60,7 @@ describe('Identities routes', () => { const identity = await fixtures.identity(knex, { block_hash: block.hash }) const { alias } = await fixtures.identity_alias(knex, { - alias: 'test', + alias: 'test.dash', identity } ) From 4fd87d9efec6b3cf394573f4b0489efbeac151d5 Mon Sep 17 00:00:00 2001 From: owl352 Date: Thu, 7 Nov 2024 20:50:43 +0300 Subject: [PATCH 06/13] initial commit --- packages/api/README.md | 6 +++-- packages/api/src/dao/TransactionsDAO.js | 6 ++--- packages/api/src/models/Transaction.js | 8 +++--- .../api/test/integration/transactions.spec.js | 18 ++++++++----- packages/frontend/src/app/api/content.md | 25 +++++++++++++------ 5 files changed, 42 insertions(+), 21 deletions(-) diff --git a/packages/api/README.md b/packages/api/README.md index f7b839ec2..d0846daa9 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -412,7 +412,8 @@ GET /transaction/DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEE type: 0, gasUsed: 1337000, status: "SUCCESS", - error: null + error: null, + owner: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs" } ``` @@ -450,7 +451,8 @@ GET /transactions?=1&limit=10&order=asc type: 0, gasUsed: 1337000, status: "SUCCESS", - error: null + error: null, + owner: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs" }, ... ] } diff --git a/packages/api/src/dao/TransactionsDAO.js b/packages/api/src/dao/TransactionsDAO.js index b5d0e87ea..4aac6a5eb 100644 --- a/packages/api/src/dao/TransactionsDAO.js +++ b/packages/api/src/dao/TransactionsDAO.js @@ -12,7 +12,7 @@ module.exports = class TransactionsDAO { .select('state_transitions.hash as tx_hash', 'state_transitions.data as data', 'state_transitions.gas_used as gas_used', 'state_transitions.status as status', 'state_transitions.error as error', 'state_transitions.type as type', 'state_transitions.index as index', 'blocks.height as block_height', - 'blocks.hash as block_hash', 'blocks.timestamp as timestamp') + 'blocks.hash as block_hash', 'blocks.timestamp as timestamp', 'state_transitions.owner as owner') .whereILike('state_transitions.hash', hash) .leftJoin('blocks', 'blocks.hash', 'state_transitions.block_hash') @@ -31,13 +31,13 @@ module.exports = class TransactionsDAO { .select(this.knex('state_transitions').count('hash').as('total_count'), 'state_transitions.hash as tx_hash', 'state_transitions.data as data', 'state_transitions.type as type', 'state_transitions.index as index', 'state_transitions.gas_used as gas_used', 'state_transitions.status as status', 'state_transitions.error as error', - 'state_transitions.block_hash as block_hash', 'state_transitions.id as id') + 'state_transitions.block_hash as block_hash', 'state_transitions.id as id', 'state_transitions.owner as owner') .select(this.knex.raw(`rank() over (order by state_transitions.id ${order}) rank`)) .as('state_transitions') const rows = await this.knex(subquery) .select('total_count', 'data', 'type', 'index', 'rank', 'block_hash', 'state_transitions.tx_hash as tx_hash', - 'gas_used', 'status', 'error', 'blocks.height as block_height', 'blocks.timestamp as timestamp') + 'gas_used', 'status', 'error', 'blocks.height as block_height', 'blocks.timestamp as timestamp', 'owner') .leftJoin('blocks', 'blocks.hash', 'block_hash') .whereBetween('rank', [fromRank, toRank]) .orderBy('state_transitions.id', order) diff --git a/packages/api/src/models/Transaction.js b/packages/api/src/models/Transaction.js index bf062ff2e..b195a28ed 100644 --- a/packages/api/src/models/Transaction.js +++ b/packages/api/src/models/Transaction.js @@ -13,8 +13,9 @@ module.exports = class Transaction { gasUsed status error + owner - constructor (hash, index, blockHash, blockHeight, type, data, timestamp, gasUsed, status, error) { + constructor (hash, index, blockHash, blockHeight, type, data, timestamp, gasUsed, status, error, owner) { this.hash = hash ?? null this.index = index ?? null this.blockHash = blockHash ?? null @@ -25,10 +26,11 @@ module.exports = class Transaction { this.gasUsed = gasUsed ?? null this.status = status ?? null this.error = error ?? null + this.owner = owner ?? null } // eslint-disable-next-line camelcase - static fromRow ({ tx_hash, index, block_hash, block_height, type, data, timestamp, gas_used, status, error }) { + static fromRow ({ tx_hash, index, block_hash, block_height, type, data, timestamp, gas_used, status, error, owner }) { let decodedError = null try { @@ -41,6 +43,6 @@ module.exports = class Transaction { decodedError = 'Cannot deserialize' } - return new Transaction(tx_hash, index, block_hash, block_height, type, data, timestamp, parseInt(gas_used), status, decodedError ?? error) + return new Transaction(tx_hash, index, block_hash, block_height, type, data, timestamp, parseInt(gas_used), status, decodedError ?? error, owner) } } diff --git a/packages/api/test/integration/transactions.spec.js b/packages/api/test/integration/transactions.spec.js index 672d412f5..f1df2708d 100644 --- a/packages/api/test/integration/transactions.spec.js +++ b/packages/api/test/integration/transactions.spec.js @@ -104,7 +104,8 @@ describe('Transaction routes', () => { type: transaction.transaction.type, gasUsed: transaction.transaction.gas_used, status: transaction.transaction.status, - error: transaction.transaction.error + error: transaction.transaction.error, + owner: transaction.transaction.owner } assert.deepEqual(expectedTransaction, body) @@ -126,7 +127,8 @@ describe('Transaction routes', () => { type: transaction.transaction.type, gasUsed: 0, status: 'FAIL', - error: 'Cannot deserialize' + error: 'Cannot deserialize', + owner: transaction.transaction.owner } assert.deepEqual(expectedTransaction, body) @@ -162,7 +164,8 @@ describe('Transaction routes', () => { type: transaction.transaction.type, gasUsed: transaction.transaction.gas_used, status: transaction.transaction.status, - error: transaction.transaction.error + error: transaction.transaction.error, + owner: transaction.transaction.owner })) assert.deepEqual(expectedTransactions, body.resultSet) @@ -191,7 +194,8 @@ describe('Transaction routes', () => { type: transaction.transaction.type, gasUsed: transaction.transaction.gas_used, status: transaction.transaction.status, - error: transaction.transaction.error + error: transaction.transaction.error, + owner: transaction.transaction.owner })) assert.deepEqual(expectedTransactions, body.resultSet) @@ -220,7 +224,8 @@ describe('Transaction routes', () => { type: transaction.transaction.type, gasUsed: transaction.transaction.gas_used, status: transaction.transaction.status, - error: transaction.transaction.error + error: transaction.transaction.error, + owner: transaction.transaction.owner })) assert.deepEqual(expectedTransactions, body.resultSet) @@ -249,7 +254,8 @@ describe('Transaction routes', () => { type: transaction.transaction.type, gasUsed: transaction.transaction.gas_used, status: transaction.transaction.status, - error: transaction.transaction.error + error: transaction.transaction.error, + owner: transaction.transaction.owner })) assert.deepEqual(expectedTransactions, body.resultSet) diff --git a/packages/frontend/src/app/api/content.md b/packages/frontend/src/app/api/content.md index a770c6996..f1297525d 100644 --- a/packages/frontend/src/app/api/content.md +++ b/packages/frontend/src/app/api/content.md @@ -343,9 +343,14 @@ GET /validator/F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0 ``` --- ### Validator stats by ProTxHash -Return a series data for the amount of proposed blocks by validator chart with variable timespan (1h, 24h, 3d, 1w) +Return a series data for the amount of proposed blocks by validator chart with + +* `start` lower interval threshold in ISO string ( _optional_ ) +* `end` upper interval threshold in ISO string ( _optional_ ) + + ``` -GET /validator/F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0/stats?timespan=24h +GET /validator/F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0/stats?start=2024-01-01T00:00:00&end=2025-01-01T00:00:00 [ { timestamp: "2024-06-23T13:51:44.154Z", @@ -374,7 +379,8 @@ GET /transaction/DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEE type: 0, gasUsed: 1337000, status: "SUCCESS", - error: null + error: null, + owner: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs" } ``` @@ -412,7 +418,8 @@ GET /transactions?=1&limit=10&order=asc type: 0, gasUsed: 1337000, status: "SUCCESS", - error: null + error: null, + owner: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs" }, ... ] } @@ -772,9 +779,13 @@ Response codes: 500: Internal Server Error ``` ### Transactions history -Return a series data for the amount of transactions chart with variable timespan (1h, 24h, 3d, 1w) +Return a series data for the amount of transactions chart + +* `start` lower interval threshold in ISO string ( _optional_ ) +* `end` upper interval threshold in ISO string ( _optional_ ) + ``` -GET /transactions/history?timespan=1h +GET /transactions/history?start=2024-01-01T00:00:00&end=2025-01-01T00:00:00 [ { timestamp: "2024-04-22T08:45:20.911Z", @@ -797,7 +808,7 @@ GET /transactions/history?timespan=1h Response codes: ``` 200: OK -400: Invalid input, check timespan value +400: Invalid input, check start/end values 500: Internal Server Error ``` ### Rate From 31250288f757a7d94c2f35a25b595c8eda68f9e6 Mon Sep 17 00:00:00 2001 From: owl352 Date: Thu, 7 Nov 2024 21:01:14 +0300 Subject: [PATCH 07/13] search and txs by identity fix --- packages/api/README.md | 3 ++- packages/api/src/dao/IdentitiesDAO.js | 5 +++-- packages/api/test/integration/identities.spec.js | 12 ++++++++---- packages/api/test/integration/main.spec.js | 3 ++- packages/frontend/src/app/api/content.md | 3 ++- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/api/README.md b/packages/api/README.md index d0846daa9..1b1f7fa66 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -770,7 +770,8 @@ GET /identities/GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec/transactions?page=1 timestamp: "2024-03-18T10:13:54.150Z", gasUsed: 1337000, status: "SUCCESS", - error: null + error: null, + owner: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec" }, ... ] } diff --git a/packages/api/src/dao/IdentitiesDAO.js b/packages/api/src/dao/IdentitiesDAO.js index 5ce47094d..020e1a772 100644 --- a/packages/api/src/dao/IdentitiesDAO.js +++ b/packages/api/src/dao/IdentitiesDAO.js @@ -246,14 +246,15 @@ module.exports = class IdentitiesDAO { const subquery = this.knex('state_transitions') .select('state_transitions.id as state_transition_id', 'state_transitions.hash as tx_hash', 'state_transitions.index as index', 'state_transitions.type as type', 'state_transitions.block_hash as block_hash', - 'state_transitions.gas_used as gas_used', 'state_transitions.status as status', 'state_transitions.error as error' + 'state_transitions.gas_used as gas_used', 'state_transitions.status as status', 'state_transitions.error as error', + 'state_transitions.owner as owner' ) .select(this.knex.raw(`rank() over (order by state_transitions.id ${order}) rank`)) .where('state_transitions.owner', '=', identifier) const rows = await this.knex.with('with_alias', subquery) .select('state_transition_id', 'tx_hash', 'index', 'block_hash', 'type', 'rank', - 'gas_used', 'status', 'gas_used', + 'gas_used', 'status', 'gas_used', 'owner', 'blocks.timestamp as timestamp', 'blocks.height as block_height') .select(this.knex('with_alias').count('*').as('total_count')) .leftJoin('blocks', 'blocks.hash', 'block_hash') diff --git a/packages/api/test/integration/identities.spec.js b/packages/api/test/integration/identities.spec.js index abbb4c1e1..5328ff4de 100644 --- a/packages/api/test/integration/identities.spec.js +++ b/packages/api/test/integration/identities.spec.js @@ -886,7 +886,8 @@ describe('Identities routes', () => { timestamp: _transaction.block.timestamp.toISOString(), gasUsed: _transaction.transaction.gas_used, status: _transaction.transaction.status, - error: _transaction.transaction.error + error: _transaction.transaction.error, + owner: _transaction.transaction.owner })) assert.deepEqual(body.resultSet, expectedTransactions) @@ -929,7 +930,8 @@ describe('Identities routes', () => { timestamp: _transaction.block.timestamp.toISOString(), gasUsed: _transaction.transaction.gas_used, status: _transaction.transaction.status, - error: _transaction.transaction.error + error: _transaction.transaction.error, + owner: _transaction.transaction.owner })) assert.deepEqual(body.resultSet, expectedTransactions) @@ -972,7 +974,8 @@ describe('Identities routes', () => { timestamp: _transaction.block.timestamp.toISOString(), gasUsed: _transaction.transaction.gas_used, status: _transaction.transaction.status, - error: _transaction.transaction.error + error: _transaction.transaction.error, + owner: _transaction.transaction.owner })) assert.deepEqual(body.resultSet, expectedTransactions) @@ -1015,7 +1018,8 @@ describe('Identities routes', () => { timestamp: _transaction.block.timestamp.toISOString(), gasUsed: _transaction.transaction.gas_used, status: _transaction.transaction.status, - error: _transaction.transaction.error + error: _transaction.transaction.error, + owner: _transaction.transaction.owner })) assert.deepEqual(body.resultSet, expectedTransactions) diff --git a/packages/api/test/integration/main.spec.js b/packages/api/test/integration/main.spec.js index 6600ffeba..8c0baf079 100644 --- a/packages/api/test/integration/main.spec.js +++ b/packages/api/test/integration/main.spec.js @@ -171,7 +171,8 @@ describe('Other routes', () => { timestamp: block.timestamp.toISOString(), gasUsed: dataContractTransaction.gas_used, status: dataContractTransaction.status, - error: dataContractTransaction.error + error: dataContractTransaction.error, + owner: dataContractTransaction.owner } assert.deepEqual({ transaction: expectedTransaction }, body) diff --git a/packages/frontend/src/app/api/content.md b/packages/frontend/src/app/api/content.md index f1297525d..d3a64c5c9 100644 --- a/packages/frontend/src/app/api/content.md +++ b/packages/frontend/src/app/api/content.md @@ -737,7 +737,8 @@ GET /identities/GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec/transactions?page=1 timestamp: "2024-03-18T10:13:54.150Z", gasUsed: 1337000, status: "SUCCESS", - error: null + error: null, + owner: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec" }, ... ] } From 2b5d84e694c04578ad7eaf18f8047958f8de2adb Mon Sep 17 00:00:00 2001 From: owl352 Date: Thu, 7 Nov 2024 21:18:13 +0300 Subject: [PATCH 08/13] trim --- packages/api/src/models/Transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/models/Transaction.js b/packages/api/src/models/Transaction.js index b195a28ed..50e605631 100644 --- a/packages/api/src/models/Transaction.js +++ b/packages/api/src/models/Transaction.js @@ -43,6 +43,6 @@ module.exports = class Transaction { decodedError = 'Cannot deserialize' } - return new Transaction(tx_hash, index, block_hash, block_height, type, data, timestamp, parseInt(gas_used), status, decodedError ?? error, owner) + return new Transaction(tx_hash, index, block_hash, block_height, type, data, timestamp, parseInt(gas_used), status, decodedError ?? error, owner?.trim()) } } From d366c6d9d35a2f47d1488f15401617fd27be40b7 Mon Sep 17 00:00:00 2001 From: owl352 Date: Thu, 7 Nov 2024 21:19:31 +0300 Subject: [PATCH 09/13] trim --- packages/api/src/models/Transaction.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/api/src/models/Transaction.js b/packages/api/src/models/Transaction.js index 50e605631..2d73cfe62 100644 --- a/packages/api/src/models/Transaction.js +++ b/packages/api/src/models/Transaction.js @@ -1,6 +1,6 @@ const cbor = require('cbor') -const { deserializeConsensusError } = require('dash').PlatformProtocol +const {deserializeConsensusError} = require('dash').PlatformProtocol module.exports = class Transaction { hash @@ -15,7 +15,7 @@ module.exports = class Transaction { error owner - constructor (hash, index, blockHash, blockHeight, type, data, timestamp, gasUsed, status, error, owner) { + constructor(hash, index, blockHash, blockHeight, type, data, timestamp, gasUsed, status, error, owner) { this.hash = hash ?? null this.index = index ?? null this.blockHash = blockHash ?? null @@ -26,16 +26,16 @@ module.exports = class Transaction { this.gasUsed = gasUsed ?? null this.status = status ?? null this.error = error ?? null - this.owner = owner ?? null + this.owner = owner ? owner.trim() : null } // eslint-disable-next-line camelcase - static fromRow ({ tx_hash, index, block_hash, block_height, type, data, timestamp, gas_used, status, error, owner }) { + static fromRow({tx_hash, index, block_hash, block_height, type, data, timestamp, gas_used, status, error, owner}) { let decodedError = null try { if (typeof error === 'string') { - const { serializedError } = cbor.decode(Buffer.from(error, 'base64'))?.data + const {serializedError} = cbor.decode(Buffer.from(error, 'base64'))?.data decodedError = deserializeConsensusError(serializedError).message } } catch (e) { @@ -43,6 +43,6 @@ module.exports = class Transaction { decodedError = 'Cannot deserialize' } - return new Transaction(tx_hash, index, block_hash, block_height, type, data, timestamp, parseInt(gas_used), status, decodedError ?? error, owner?.trim()) + return new Transaction(tx_hash, index, block_hash, block_height, type, data, timestamp, parseInt(gas_used), status, decodedError ?? error, owner) } } From 604523105b4e928ba7fe1e599fc4c96c57676c4a Mon Sep 17 00:00:00 2001 From: owl352 Date: Thu, 7 Nov 2024 21:24:44 +0300 Subject: [PATCH 10/13] lint --- packages/api/src/models/Transaction.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/api/src/models/Transaction.js b/packages/api/src/models/Transaction.js index 2d73cfe62..69e3ec1f6 100644 --- a/packages/api/src/models/Transaction.js +++ b/packages/api/src/models/Transaction.js @@ -1,6 +1,6 @@ const cbor = require('cbor') -const {deserializeConsensusError} = require('dash').PlatformProtocol +const { deserializeConsensusError } = require('dash').PlatformProtocol module.exports = class Transaction { hash @@ -15,7 +15,7 @@ module.exports = class Transaction { error owner - constructor(hash, index, blockHash, blockHeight, type, data, timestamp, gasUsed, status, error, owner) { + constructor (hash, index, blockHash, blockHeight, type, data, timestamp, gasUsed, status, error, owner) { this.hash = hash ?? null this.index = index ?? null this.blockHash = blockHash ?? null @@ -30,12 +30,12 @@ module.exports = class Transaction { } // eslint-disable-next-line camelcase - static fromRow({tx_hash, index, block_hash, block_height, type, data, timestamp, gas_used, status, error, owner}) { + static fromRow ({ tx_hash, index, block_hash, block_height, type, data, timestamp, gas_used, status, error, owner }) { let decodedError = null try { if (typeof error === 'string') { - const {serializedError} = cbor.decode(Buffer.from(error, 'base64'))?.data + const { serializedError } = cbor.decode(Buffer.from(error, 'base64'))?.data decodedError = deserializeConsensusError(serializedError).message } } catch (e) { From afb8a4448349352bfd204ce5d48c37de1f99bf34 Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 9 Nov 2024 01:47:16 +0300 Subject: [PATCH 11/13] added aliases --- packages/api/package.json | 4 +-- .../src/controllers/TransactionsController.js | 31 +++++++++++++--- packages/api/src/dao/TransactionsDAO.js | 27 +++++++++++--- packages/api/src/models/Transaction.js | 31 +++++++++++++--- packages/api/src/server.js | 13 ++++--- .../api/test/integration/transactions.spec.js | 36 +++++++++++++++---- 6 files changed, 114 insertions(+), 28 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index 39de5ea35..e5c8ecb71 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -12,7 +12,6 @@ "lint": "standard ." }, "dependencies": { - "@dashevo/dapi-client": "github:owl352/dapi-client", "@dashevo/dashd-rpc": "19.0.0", "@fastify/cors": "^8.3.0", "@scure/base": "^1.1.5", @@ -24,7 +23,8 @@ "fastify-metrics": "^11.0.0", "knex": "^2.5.1", "node-fetch": "^2.6.11", - "pg": "^8.11.3" + "pg": "^8.11.3", + "@dashevo/dapi-client": "github:owl352/dapi-client" }, "devDependencies": { "standard": "^17.1.0", diff --git a/packages/api/src/controllers/TransactionsController.js b/packages/api/src/controllers/TransactionsController.js index c792866c6..da88ffa0e 100644 --- a/packages/api/src/controllers/TransactionsController.js +++ b/packages/api/src/controllers/TransactionsController.js @@ -1,11 +1,12 @@ const TransactionsDAO = require('../dao/TransactionsDAO') const utils = require('../utils') -const { calculateInterval } = require('../utils') +const { calculateInterval, validateAliases } = require('../utils') class TransactionsController { - constructor (client, knex) { + constructor (client, knex, dapi) { this.client = client this.transactionsDAO = new TransactionsDAO(knex) + this.dapi = dapi } getTransactionByHash = async (request, reply) => { @@ -17,7 +18,17 @@ class TransactionsController { return reply.status(404).send({ message: 'not found' }) } - reply.send(transaction) + const validatedAliases = transaction.owner.aliases?.length > 0 + ? await validateAliases(transaction.owner.aliases ?? [], transaction.owner?.identifier, this.dapi) + : [] + + reply.send({ + ...transaction, + owner: { + ...transaction.owner, + aliases: validatedAliases + } + }) } getTransactions = async (request, response) => { @@ -29,7 +40,19 @@ class TransactionsController { const transactions = await this.transactionsDAO.getTransactions(Number(page ?? 1), Number(limit ?? 10), order) - response.send(transactions) + const transactionsWithCorrectAliases = await Promise.all(transactions.resultSet.map(async transaction => + ({ + ...transaction, + owner: { + ...transaction.owner, + aliases: transaction.owner.aliases?.length > 0 + ? await validateAliases(transaction.owner.aliases ?? [], transaction.owner?.identifier, this.dapi) + : [] + } + }) + )) + + response.send({ ...transactions, resultSet: transactionsWithCorrectAliases }) } getTransactionHistory = async (request, response) => { diff --git a/packages/api/src/dao/TransactionsDAO.js b/packages/api/src/dao/TransactionsDAO.js index 4aac6a5eb..156d48a66 100644 --- a/packages/api/src/dao/TransactionsDAO.js +++ b/packages/api/src/dao/TransactionsDAO.js @@ -8,13 +8,23 @@ module.exports = class TransactionsDAO { } getTransactionByHash = async (hash) => { + const aliasesSubquery = this.knex('identity_aliases') + .select('identity_identifier', this.knex.raw('array_agg(alias) as aliases')) + .groupBy('identity_identifier') + .as('aliases') + const [row] = await this.knex('state_transitions') - .select('state_transitions.hash as tx_hash', 'state_transitions.data as data', - 'state_transitions.gas_used as gas_used', 'state_transitions.status as status', 'state_transitions.error as error', - 'state_transitions.type as type', 'state_transitions.index as index', 'blocks.height as block_height', - 'blocks.hash as block_hash', 'blocks.timestamp as timestamp', 'state_transitions.owner as owner') + .select( + 'state_transitions.hash as tx_hash', 'state_transitions.data as data', + 'state_transitions.gas_used as gas_used', 'state_transitions.status as status', + 'state_transitions.error as error', 'state_transitions.type as type', + 'state_transitions.index as index', 'blocks.height as block_height', + 'blocks.hash as block_hash', 'blocks.timestamp as timestamp', 'state_transitions.owner as owner', + 'aliases.aliases as aliases' + ) .whereILike('state_transitions.hash', hash) .leftJoin('blocks', 'blocks.hash', 'state_transitions.block_hash') + .leftJoin(aliasesSubquery, 'aliases.identity_identifier', 'owner') if (!row) { return null @@ -27,17 +37,24 @@ module.exports = class TransactionsDAO { const fromRank = ((page - 1) * limit) + 1 const toRank = fromRank + limit - 1 + const aliasesSubquery = this.knex('identity_aliases') + .select('identity_identifier', this.knex.raw('array_agg(alias) as aliases')) + .groupBy('identity_identifier') + .as('aliases') + const subquery = this.knex('state_transitions') .select(this.knex('state_transitions').count('hash').as('total_count'), 'state_transitions.hash as tx_hash', 'state_transitions.data as data', 'state_transitions.type as type', 'state_transitions.index as index', 'state_transitions.gas_used as gas_used', 'state_transitions.status as status', 'state_transitions.error as error', 'state_transitions.block_hash as block_hash', 'state_transitions.id as id', 'state_transitions.owner as owner') .select(this.knex.raw(`rank() over (order by state_transitions.id ${order}) rank`)) + .select('aliases') + .leftJoin(aliasesSubquery, 'aliases.identity_identifier', 'state_transitions.owner') .as('state_transitions') const rows = await this.knex(subquery) .select('total_count', 'data', 'type', 'index', 'rank', 'block_hash', 'state_transitions.tx_hash as tx_hash', - 'gas_used', 'status', 'error', 'blocks.height as block_height', 'blocks.timestamp as timestamp', 'owner') + 'gas_used', 'status', 'error', 'blocks.height as block_height', 'blocks.timestamp as timestamp', 'owner', 'aliases') .leftJoin('blocks', 'blocks.hash', 'block_hash') .whereBetween('rank', [fromRank, toRank]) .orderBy('state_transitions.id', order) diff --git a/packages/api/src/models/Transaction.js b/packages/api/src/models/Transaction.js index 69e3ec1f6..d7b133651 100644 --- a/packages/api/src/models/Transaction.js +++ b/packages/api/src/models/Transaction.js @@ -26,11 +26,24 @@ module.exports = class Transaction { this.gasUsed = gasUsed ?? null this.status = status ?? null this.error = error ?? null - this.owner = owner ? owner.trim() : null + this.owner = owner || null } - // eslint-disable-next-line camelcase - static fromRow ({ tx_hash, index, block_hash, block_height, type, data, timestamp, gas_used, status, error, owner }) { + /* eslint-disable camelcase */ + static fromRow ({ + tx_hash, + index, + block_hash, + block_height, + type, + data, + timestamp, + gas_used, + status, + error, + owner, + aliases + }) { let decodedError = null try { @@ -43,6 +56,16 @@ module.exports = class Transaction { decodedError = 'Cannot deserialize' } - return new Transaction(tx_hash, index, block_hash, block_height, type, data, timestamp, parseInt(gas_used), status, decodedError ?? error, owner) + return new Transaction( + tx_hash, index, block_hash, + block_height, type, data, + timestamp, parseInt(gas_used), + status, decodedError ?? error, + aliases + ? { + identifier: owner, + aliases + } + : owner) } } diff --git a/packages/api/src/server.js b/packages/api/src/server.js index 943318896..25c499f84 100644 --- a/packages/api/src/server.js +++ b/packages/api/src/server.js @@ -16,7 +16,6 @@ const ValidatorsController = require('./controllers/ValidatorsController') const { getKnex } = require('./utils') const BlocksDAO = require('./dao/BlocksDAO') const DAPI = require('./DAPI') -const DAPIClient = require('@dashevo/dapi-client') const RateController = require('./controllers/RateController') const { default: loadWasmDpp } = require('dash').PlatformProtocol @@ -45,16 +44,16 @@ let dapi module.exports = { start: async () => { - client = new Dash.Client() + client = new Dash.Client({ + dapiAddresses: (process.env.DAPI_URL ?? '127.0.0.1:1443:self-signed').split(','), + network: process.env.NETWORK ?? 'testnet' + }) await loadWasmDpp() await client.platform.initialize() - const dapiClient = new DAPIClient({ - dapiAddresses: (process.env.DAPI_URL ?? '127.0.0.1:1443:self-signed').split(','), - network: process.env.NETWORK ?? 'testnet' - }) + const dapiClient = client.getDAPIClient() const { dpp } = client.platform @@ -79,7 +78,7 @@ module.exports = { const mainController = new MainController(knex, dapi) const epochController = new EpochController(knex, dapi) const blocksController = new BlocksController(knex) - const transactionsController = new TransactionsController(client, knex) + const transactionsController = new TransactionsController(client, knex, dapi) const dataContractsController = new DataContractsController(knex) const documentsController = new DocumentsController(knex) const identitiesController = new IdentitiesController(knex, dapi) diff --git a/packages/api/test/integration/transactions.spec.js b/packages/api/test/integration/transactions.spec.js index f1df2708d..4b01ab46c 100644 --- a/packages/api/test/integration/transactions.spec.js +++ b/packages/api/test/integration/transactions.spec.js @@ -6,6 +6,7 @@ const { getKnex } = require('../../src/utils') const fixtures = require('../utils/fixtures') const StateTransitionEnum = require('../../src/enums/StateTransitionEnum') const tenderdashRpc = require('../../src/tenderdashRpc') +const DAPI = require('../../src/DAPI') describe('Transaction routes', () => { let app @@ -13,6 +14,7 @@ describe('Transaction routes', () => { let knex let identity + let identityAlias let block let transactions @@ -25,6 +27,8 @@ describe('Transaction routes', () => { } })) + mock.method(DAPI.prototype, 'getContestedState', async () => null) + const startDate = new Date(new Date() - 1000 * 60 * 60) app = await server.start() @@ -38,6 +42,8 @@ describe('Transaction routes', () => { }) identity = await fixtures.identity(knex, { block_hash: block.hash }) + identityAlias = await fixtures.identity_alias(knex, { alias: 'test.dash', identity }) + transactions = [{ transaction: identity.transaction, block }] // error tx @@ -105,7 +111,10 @@ describe('Transaction routes', () => { gasUsed: transaction.transaction.gas_used, status: transaction.transaction.status, error: transaction.transaction.error, - owner: transaction.transaction.owner + owner: { + identifier: transaction.transaction.owner, + aliases: [identityAlias.alias] + } } assert.deepEqual(expectedTransaction, body) @@ -128,7 +137,10 @@ describe('Transaction routes', () => { gasUsed: 0, status: 'FAIL', error: 'Cannot deserialize', - owner: transaction.transaction.owner + owner: { + identifier: transaction.transaction.owner, + aliases: [identityAlias.alias] + } } assert.deepEqual(expectedTransaction, body) @@ -165,7 +177,10 @@ describe('Transaction routes', () => { gasUsed: transaction.transaction.gas_used, status: transaction.transaction.status, error: transaction.transaction.error, - owner: transaction.transaction.owner + owner: { + identifier: transaction.transaction.owner, + aliases: [identityAlias.alias] + } })) assert.deepEqual(expectedTransactions, body.resultSet) @@ -195,7 +210,10 @@ describe('Transaction routes', () => { gasUsed: transaction.transaction.gas_used, status: transaction.transaction.status, error: transaction.transaction.error, - owner: transaction.transaction.owner + owner: { + identifier: transaction.transaction.owner, + aliases: [identityAlias.alias] + } })) assert.deepEqual(expectedTransactions, body.resultSet) @@ -225,7 +243,10 @@ describe('Transaction routes', () => { gasUsed: transaction.transaction.gas_used, status: transaction.transaction.status, error: transaction.transaction.error, - owner: transaction.transaction.owner + owner: { + identifier: transaction.transaction.owner, + aliases: [identityAlias.alias] + } })) assert.deepEqual(expectedTransactions, body.resultSet) @@ -255,7 +276,10 @@ describe('Transaction routes', () => { gasUsed: transaction.transaction.gas_used, status: transaction.transaction.status, error: transaction.transaction.error, - owner: transaction.transaction.owner + owner: { + identifier: transaction.transaction.owner, + aliases: [identityAlias.alias] + } })) assert.deepEqual(expectedTransactions, body.resultSet) From 42b93aa5b0039369200e9de5f51ba8de841c7e35 Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 9 Nov 2024 01:49:27 +0300 Subject: [PATCH 12/13] README.md update --- packages/api/README.md | 10 ++++++++-- packages/frontend/src/app/api/content.md | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/api/README.md b/packages/api/README.md index 1b1f7fa66..28a0013d4 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -413,7 +413,10 @@ GET /transaction/DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEE gasUsed: 1337000, status: "SUCCESS", error: null, - owner: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs" + owner: { + identifier: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs", + aliases: [] + } } ``` @@ -452,7 +455,10 @@ GET /transactions?=1&limit=10&order=asc gasUsed: 1337000, status: "SUCCESS", error: null, - owner: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs" + owner: { + identifier: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs", + aliases: [] + } }, ... ] } diff --git a/packages/frontend/src/app/api/content.md b/packages/frontend/src/app/api/content.md index d3a64c5c9..c1bd824a2 100644 --- a/packages/frontend/src/app/api/content.md +++ b/packages/frontend/src/app/api/content.md @@ -380,7 +380,10 @@ GET /transaction/DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEE gasUsed: 1337000, status: "SUCCESS", error: null, - owner: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs" + owner: { + identifier: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs", + aliases: [] + } } ``` @@ -419,7 +422,10 @@ GET /transactions?=1&limit=10&order=asc gasUsed: 1337000, status: "SUCCESS", error: null, - owner: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs" + owner: { + identifier: "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs", + aliases: [] + } }, ... ] } From 10b1d22bb75a22ef3ac8c25b85581332c61ed542 Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 9 Nov 2024 02:13:08 +0300 Subject: [PATCH 13/13] tiny fixes --- packages/api/src/models/Transaction.js | 4 ++-- packages/api/test/integration/main.spec.js | 15 +++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/api/src/models/Transaction.js b/packages/api/src/models/Transaction.js index d7b133651..0beba1206 100644 --- a/packages/api/src/models/Transaction.js +++ b/packages/api/src/models/Transaction.js @@ -63,9 +63,9 @@ module.exports = class Transaction { status, decodedError ?? error, aliases ? { - identifier: owner, + identifier: owner?.trim(), aliases } - : owner) + : owner?.trim()) } } diff --git a/packages/api/test/integration/main.spec.js b/packages/api/test/integration/main.spec.js index 3f5f78f8a..cb29c311b 100644 --- a/packages/api/test/integration/main.spec.js +++ b/packages/api/test/integration/main.spec.js @@ -119,15 +119,19 @@ describe('Other routes', () => { for (let i = 0; i < 48; i++) { const tmpBlock = await fixtures.block(knex, { - timestamp: new Date(new Date().getTime() - 3600000 * i) + timestamp: new Date(new Date().getTime() - 3600000 * i), + height: i + 10 }) + const transaction = await fixtures.transaction(knex, { block_hash: tmpBlock.hash, type: 0, owner: identity.identifier, gas_used: 10000 }) + transactions.push(transaction.hash) + blocks.push(tmpBlock) } }) @@ -174,7 +178,10 @@ describe('Other routes', () => { gasUsed: dataContractTransaction.gas_used, status: dataContractTransaction.status, error: dataContractTransaction.error, - owner: dataContractTransaction.owner + owner: { + identifier: dataContractTransaction.owner, + aliases: [identityAlias.alias] + } } assert.deepEqual({ transaction: expectedTransaction }, body) @@ -195,7 +202,7 @@ describe('Other routes', () => { l1LockedHeight: block.l1_locked_height, validator: block.validator }, - txs: transactions + txs: [identityTransaction.hash, dataContractTransaction.hash, documentTransaction.hash] } assert.deepEqual({ block: expectedBlock }, body) @@ -323,7 +330,7 @@ describe('Other routes', () => { api: { version: require('../../package.json').version, block: { - height: 10, + height: blocks.length - 1, hash: blocks[blocks.length - 1].hash, timestamp: blocks[blocks.length - 1].timestamp.toISOString() }