From 66cd711981ddbdbbc57fcfea5ffbb64eb6da099b Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 23 Nov 2024 18:07:47 +0300 Subject: [PATCH 01/22] fixes --- .../src/controllers/TransactionsController.js | 30 ++++++++++++++- packages/api/src/dao/TransactionsDAO.js | 37 +++++++++++++++++-- packages/api/src/schemas.js | 13 +++++++ 3 files changed, 75 insertions(+), 5 deletions(-) diff --git a/packages/api/src/controllers/TransactionsController.js b/packages/api/src/controllers/TransactionsController.js index fab2145a2..1cb8ac88c 100644 --- a/packages/api/src/controllers/TransactionsController.js +++ b/packages/api/src/controllers/TransactionsController.js @@ -2,6 +2,7 @@ const TransactionsDAO = require('../dao/TransactionsDAO') const utils = require('../utils') const { calculateInterval, iso8601duration } = require('../utils') const Intervals = require('../enums/IntervalsEnum') +const StateTransitionEnum = require('../enums/StateTransitionEnum') class TransactionsController { constructor (client, knex, dapi) { @@ -23,13 +24,38 @@ class TransactionsController { } getTransactions = async (request, response) => { - const { page = 1, limit = 10, order = 'asc' } = request.query + const { + page = 1, + limit = 10, + order = 'asc', + filters = [-1], + owner, + status = 'ALL' + } = request.query if (order !== 'asc' && order !== 'desc') { return response.status(400).send({ message: `invalid ordering value ${order}. only 'asc' or 'desc' is valid values` }) } - const transactions = await this.transactionsDAO.getTransactions(Number(page ?? 1), Number(limit ?? 10), order) + const stateTransitionIndexes = Object.entries(StateTransitionEnum).map(([, entry]) => entry) + + const validatedFilters = + filters.map((filter) => + stateTransitionIndexes.includes(filter) || filter === -1 + ) + + if (validatedFilters.includes(false) || filters.length === 0 || typeof filters !== 'object') { + return response.status(400).send({ message: 'invalid filters values' }) + } + + const transactions = await this.transactionsDAO.getTransactions( + Number(page ?? 1), + Number(limit ?? 10), + order, + filters, + owner, + status + ) response.send(transactions) } diff --git a/packages/api/src/dao/TransactionsDAO.js b/packages/api/src/dao/TransactionsDAO.js index c2d7ec56a..f292c291d 100644 --- a/packages/api/src/dao/TransactionsDAO.js +++ b/packages/api/src/dao/TransactionsDAO.js @@ -49,22 +49,52 @@ module.exports = class TransactionsDAO { return Transaction.fromRow({ ...row, aliases }) } - getTransactions = async (page, limit, order) => { + getTransactions = async (page, limit, order, filters, owner, status) => { const fromRank = ((page - 1) * limit) + 1 const toRank = fromRank + limit - 1 + let filtersQuery = '' + const filtersBindings = [] + + if (!filters.includes(-1)) { + // Currently knex cannot digest an array of numbers correctly + // https://github.com/knex/knex/issues/2060 + filtersQuery = filters.length > 1 ? `type in (${filters.join(',')})` : `type = ${filters[0]}` + } + + if (owner) { + filtersBindings.push(owner) + filtersQuery = filtersQuery !== '' ? filtersQuery + ' AND owner = ?' : 'owner = ?' + } + + if (status !== 'ALL') { + filtersBindings.push(status) + filtersQuery = filtersQuery !== '' ? filtersQuery + ' and status = ?' : 'status = ?' + } + 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') + const filtersSubquery = 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') + .whereRaw(filtersQuery, filtersBindings) + .as('state_transitions') + + console.log(filtersSubquery.toString()) + + const subquery = this.knex(filtersSubquery) + .select('tx_hash', 'total_count', + 'data', 'type', 'index', + 'gas_used', 'status', 'error', + 'block_hash', 'id', 'owner', + 'identity_identifier', 'aliases' + ) .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') @@ -74,6 +104,7 @@ module.exports = class TransactionsDAO { .leftJoin('blocks', 'blocks.hash', 'block_hash') .whereBetween('rank', [fromRank, toRank]) .orderBy('state_transitions.id', order) + const totalCount = rows.length > 0 ? Number(rows[0].total_count) : 0 const resultSet = await Promise.all(rows.map(async (row) => { diff --git a/packages/api/src/schemas.js b/packages/api/src/schemas.js index 7454dce45..3e24aaf0f 100644 --- a/packages/api/src/schemas.js +++ b/packages/api/src/schemas.js @@ -31,6 +31,19 @@ const schemaTypes = [ type: ['integer', 'null'], minimum: 0, maximum: 8 + }, + filters: { + type: ['array', 'null'], + items: { + type: 'number' + } + }, + status: { + type: ['string', 'null'], + enum: ['SUCCESS', 'FAIL', 'ALL'] + }, + owner: { + type: ['string', 'null'] } } }, From 74758a5d56f1ee35cc88276f4caafbb661060568 Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 23 Nov 2024 18:21:09 +0300 Subject: [PATCH 02/22] `README.md` update --- packages/api/README.md | 5 ++++- packages/frontend/src/app/api/content.md | 12 +++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/api/README.md b/packages/api/README.md index 17f884fcd..c75e638fa 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -459,9 +459,12 @@ Return transaction set paged Status can be either `SUCCESS` or `FAIL`. In case of error tx, message will appear in the `error` field as Base64 string * `limit` cannot be more then 100 +* `owner` Identity identifier +* `status` can be `SUCCESS`, `FAIL` or `ALL` +* `filters` array of transactions types ``` -GET /transactions?=1&limit=10&order=asc +GET /transactions?=1&limit=10&order=asc&6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL { pagination: { diff --git a/packages/frontend/src/app/api/content.md b/packages/frontend/src/app/api/content.md index d39e894cd..dab24e89f 100644 --- a/packages/frontend/src/app/api/content.md +++ b/packages/frontend/src/app/api/content.md @@ -15,6 +15,8 @@ Reference: * [Blocks](#blocks) * [Validators](#validators) * [Validator by ProTxHash](#validator-by-protxhash) +* [Validator Blocks Statistic](#validator-stats-by-protxhash) +* [Validator Rewards Statistic](#validator-rewards-stats-by-protxhash) * [Transaction by hash](#transaction-by-hash) * [Transactions](#transactions) * [Data Contract By Identifier](#data-contract-by-identifier) @@ -424,9 +426,12 @@ Return transaction set paged Status can be either `SUCCESS` or `FAIL`. In case of error tx, message will appear in the `error` field as Base64 string * `limit` cannot be more then 100 +* `owner` Identity identifier +* `status` can be `SUCCESS`, `FAIL` or `ALL` +* `filters` array of transactions types ``` -GET /transactions?=1&limit=10&order=asc +GET /transactions?=1&limit=10&order=asc&6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL { pagination: { @@ -826,10 +831,7 @@ GET /identities/GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec/transactions?page=1 gasUsed: 1337000, status: "SUCCESS", error: null, - owner: { - identifier: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec", - aliases: [] - } + owner: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec" }, ... ] } From 2d0f6d167f2b047fbb88904486646bc35bd0946d Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 23 Nov 2024 18:27:40 +0300 Subject: [PATCH 03/22] tests --- .../api/test/integration/transactions.spec.js | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/packages/api/test/integration/transactions.spec.js b/packages/api/test/integration/transactions.spec.js index c6dd83a1c..daf765ab9 100644 --- a/packages/api/test/integration/transactions.spec.js +++ b/packages/api/test/integration/transactions.spec.js @@ -231,6 +231,94 @@ describe('Transaction routes', () => { assert.deepEqual(expectedTransactions, body.resultSet) }) + it('should return default set of transactions desc with owner', async () => { + const owner = transactions[0].transaction.owner + + const { body } = await client.get(`/transactions?order=desc&owner=${owner}`) + .expect(200) + .expect('Content-Type', 'application/json; charset=utf-8') + + assert.equal(body.resultSet.length, 10) + assert.equal(body.pagination.total, transactions.length) + assert.equal(body.pagination.page, 1) + assert.equal(body.pagination.limit, 10) + + const expectedTransactions = transactions + .filter(transaction => transaction.transaction.owner === owner) + .sort((a, b) => b.transaction.id - a.transaction.id) + .slice(0, 10) + .map(transaction => ({ + blockHash: transaction.block.hash, + blockHeight: transaction.block.height, + data: '{}', + hash: transaction.transaction.hash, + index: transaction.transaction.index, + timestamp: transaction.block.timestamp.toISOString(), + type: transaction.transaction.type, + gasUsed: transaction.transaction.gas_used, + status: transaction.transaction.status, + error: transaction.transaction.error, + owner: { + identifier: transaction.transaction.owner, + aliases: [{ + alias: identityAlias.alias, + status: 'ok' + }] + } + })) + + assert.deepEqual(expectedTransactions, body.resultSet) + }) + + it('should return default set of transactions desc with owner and type filter', async () => { + const owner = transactions[0].transaction.owner + + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&filters=0&filters=8`) + .expect(200) + .expect('Content-Type', 'application/json; charset=utf-8') + + assert.equal(body.resultSet.length, 10) + assert.equal(body.pagination.total, transactions.length) + assert.equal(body.pagination.page, 1) + assert.equal(body.pagination.limit, 10) + + const expectedTransactions = transactions + .filter(transaction => transaction.transaction.owner === owner) + .sort((a, b) => b.transaction.id - a.transaction.id) + .slice(0, 10) + .map(transaction => ({ + blockHash: transaction.block.hash, + blockHeight: transaction.block.height, + data: '{}', + hash: transaction.transaction.hash, + index: transaction.transaction.index, + timestamp: transaction.block.timestamp.toISOString(), + type: transaction.transaction.type, + gasUsed: transaction.transaction.gas_used, + status: transaction.transaction.status, + error: transaction.transaction.error, + owner: { + identifier: transaction.transaction.owner, + aliases: [{ + alias: identityAlias.alias, + status: 'ok' + }] + } + })) + + assert.deepEqual(expectedTransactions, body.resultSet) + }) + + it('should return empty set of transactions desc with owner and type filter', async () => { + const owner = transactions[0].transaction.owner + + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&filters=8`) + .expect(200) + .expect('Content-Type', 'application/json; charset=utf-8') + + assert.equal(body.resultSet.length, 0) + }) + it('should return be able to walk through pages desc', async () => { const { body } = await client.get('/transactions?page=3&limit=3&order=desc') .expect(200) From ab8cc61dcc9fee63c4d8c18ff1c587fa60766710 Mon Sep 17 00:00:00 2001 From: owl352 Date: Tue, 26 Nov 2024 22:57:34 +0300 Subject: [PATCH 04/22] README.md fix --- packages/api/README.md | 2 +- packages/frontend/src/app/api/content.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api/README.md b/packages/api/README.md index c75e638fa..48e8cf5a0 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -464,7 +464,7 @@ Status can be either `SUCCESS` or `FAIL`. In case of error tx, message will appe * `filters` array of transactions types ``` -GET /transactions?=1&limit=10&order=asc&6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL +GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL { pagination: { diff --git a/packages/frontend/src/app/api/content.md b/packages/frontend/src/app/api/content.md index dab24e89f..fa6a1cf0f 100644 --- a/packages/frontend/src/app/api/content.md +++ b/packages/frontend/src/app/api/content.md @@ -431,7 +431,7 @@ Status can be either `SUCCESS` or `FAIL`. In case of error tx, message will appe * `filters` array of transactions types ``` -GET /transactions?=1&limit=10&order=asc&6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL +GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL { pagination: { From deefea0da8607e9de5c0baaa37fa9aec44437798 Mon Sep 17 00:00:00 2001 From: owl352 Date: Fri, 29 Nov 2024 16:41:00 +0300 Subject: [PATCH 05/22] min max --- packages/api/README.md | 4 +- .../src/controllers/TransactionsController.js | 17 ++-- packages/api/src/dao/TransactionsDAO.js | 18 +++-- packages/api/src/schemas.js | 6 ++ .../api/test/integration/transactions.spec.js | 80 ++++++++++++++++++- packages/frontend/src/app/api/content.md | 6 +- 6 files changed, 114 insertions(+), 17 deletions(-) diff --git a/packages/api/README.md b/packages/api/README.md index 0745ea294..7a1a48e3f 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -464,9 +464,11 @@ Status can be either `SUCCESS` or `FAIL`. In case of error tx, message will appe * `owner` Identity identifier * `status` can be `SUCCESS`, `FAIL` or `ALL` * `filters` array of transactions types +* `min` number of min `gas_used` +* `max` number of max `gas_used` ``` -GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL +GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL&min=0&max=9999999 { pagination: { diff --git a/packages/api/src/controllers/TransactionsController.js b/packages/api/src/controllers/TransactionsController.js index 1cb8ac88c..225e9feff 100644 --- a/packages/api/src/controllers/TransactionsController.js +++ b/packages/api/src/controllers/TransactionsController.js @@ -28,9 +28,11 @@ class TransactionsController { page = 1, limit = 10, order = 'asc', - filters = [-1], + filters, owner, - status = 'ALL' + status = 'ALL', + min, + max } = request.query if (order !== 'asc' && order !== 'desc') { @@ -39,12 +41,9 @@ class TransactionsController { const stateTransitionIndexes = Object.entries(StateTransitionEnum).map(([, entry]) => entry) - const validatedFilters = - filters.map((filter) => - stateTransitionIndexes.includes(filter) || filter === -1 - ) + const validatedFilters = filters?.map((filter) => stateTransitionIndexes.includes(filter)) - if (validatedFilters.includes(false) || filters.length === 0 || typeof filters !== 'object') { + if (validatedFilters?.includes(false) || filters?.length === 0) { return response.status(400).send({ message: 'invalid filters values' }) } @@ -54,7 +53,9 @@ class TransactionsController { order, filters, owner, - status + status, + min, + max ) response.send(transactions) diff --git a/packages/api/src/dao/TransactionsDAO.js b/packages/api/src/dao/TransactionsDAO.js index a2d02bbcf..30b617e74 100644 --- a/packages/api/src/dao/TransactionsDAO.js +++ b/packages/api/src/dao/TransactionsDAO.js @@ -49,14 +49,14 @@ module.exports = class TransactionsDAO { return Transaction.fromRow({ ...row, aliases }) } - getTransactions = async (page, limit, order, filters, owner, status) => { + getTransactions = async (page, limit, order, filters, owner, status, min, max) => { const fromRank = ((page - 1) * limit) + 1 const toRank = fromRank + limit - 1 let filtersQuery = '' const filtersBindings = [] - if (!filters.includes(-1)) { + if (filters) { // Currently knex cannot digest an array of numbers correctly // https://github.com/knex/knex/issues/2060 filtersQuery = filters.length > 1 ? `type in (${filters.join(',')})` : `type = ${filters[0]}` @@ -64,7 +64,7 @@ module.exports = class TransactionsDAO { if (owner) { filtersBindings.push(owner) - filtersQuery = filtersQuery !== '' ? filtersQuery + ' AND owner = ?' : 'owner = ?' + filtersQuery = filtersQuery !== '' ? filtersQuery + ' and owner = ?' : 'owner = ?' } if (status !== 'ALL') { @@ -72,6 +72,16 @@ module.exports = class TransactionsDAO { filtersQuery = filtersQuery !== '' ? filtersQuery + ' and status = ?' : 'status = ?' } + if (min) { + filtersBindings.push(min) + filtersQuery = filtersQuery !== '' ? filtersQuery + ' and gas_used >= ?' : 'gas_used >= ?' + } + + if (max) { + filtersBindings.push(max) + filtersQuery = filtersQuery !== '' ? filtersQuery + ' and gas_used <= ?' : 'gas_used <= ?' + } + const aliasesSubquery = this.knex('identity_aliases') .select('identity_identifier', this.knex.raw('array_agg(alias) as aliases')) .groupBy('identity_identifier') @@ -85,8 +95,6 @@ module.exports = class TransactionsDAO { .whereRaw(filtersQuery, filtersBindings) .as('state_transitions') - console.log(filtersSubquery.toString()) - const subquery = this.knex(filtersSubquery) .select('tx_hash', 'total_count', 'data', 'type', 'index', diff --git a/packages/api/src/schemas.js b/packages/api/src/schemas.js index 3e24aaf0f..8752c1007 100644 --- a/packages/api/src/schemas.js +++ b/packages/api/src/schemas.js @@ -44,6 +44,12 @@ const schemaTypes = [ }, owner: { type: ['string', 'null'] + }, + min: { + type: ['number', 'null'] + }, + max: { + type: ['number', 'null'] } } }, diff --git a/packages/api/test/integration/transactions.spec.js b/packages/api/test/integration/transactions.spec.js index daf765ab9..c73e97201 100644 --- a/packages/api/test/integration/transactions.spec.js +++ b/packages/api/test/integration/transactions.spec.js @@ -63,7 +63,11 @@ describe('Transaction routes', () => { }) const transaction = await fixtures.transaction(knex, { - block_hash: block.hash, data: '{}', type: StateTransitionEnum.DATA_CONTRACT_CREATE, owner: identity.identifier + block_hash: block.hash, + data: '{}', + type: StateTransitionEnum.DATA_CONTRACT_CREATE, + owner: identity.identifier, + gas_used: i * 123 }) transactions.push({ transaction, block }) @@ -309,6 +313,80 @@ describe('Transaction routes', () => { assert.deepEqual(expectedTransactions, body.resultSet) }) + it('should return default set of transactions desc with owner and type filter and status', async () => { + const owner = transactions[0].transaction.owner + + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&filters=1&status=FAIL`) + .expect(200) + .expect('Content-Type', 'application/json; charset=utf-8') + + assert.equal(body.resultSet.length, 1) + assert.equal(body.pagination.total, transactions.length) + assert.equal(body.pagination.page, 1) + assert.equal(body.pagination.limit, 10) + + const expectedTransactions = transactions + .filter(transaction => transaction.transaction.status === 'FAIL') + .map(transaction => ({ + blockHash: transaction.block.hash, + blockHeight: transaction.block.height, + data: '{}', + hash: transaction.transaction.hash, + index: transaction.transaction.index, + timestamp: transaction.block.timestamp.toISOString(), + type: transaction.transaction.type, + gasUsed: transaction.transaction.gas_used, + status: transaction.transaction.status, + error: transaction.transaction.error, + owner: { + identifier: transaction.transaction.owner, + aliases: [{ + alias: identityAlias.alias, + status: 'ok' + }] + } + })) + + assert.deepEqual(expectedTransactions, body.resultSet) + }) + + it('should return default set of transactions desc with owner and type filter and min-max', async () => { + const owner = transactions[0].transaction.owner + + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&filters=0&min=246&max=1107`) + .expect(200) + .expect('Content-Type', 'application/json; charset=utf-8') + + assert.equal(body.resultSet.length, 8) + assert.equal(body.pagination.total, transactions.length) + assert.equal(body.pagination.page, 1) + assert.equal(body.pagination.limit, 10) + + const expectedTransactions = transactions + .filter(transaction => transaction.transaction.gas_used <= 1107 && transaction.transaction.gas_used >= 246) + .map(transaction => ({ + blockHash: transaction.block.hash, + blockHeight: transaction.block.height, + data: '{}', + hash: transaction.transaction.hash, + index: transaction.transaction.index, + timestamp: transaction.block.timestamp.toISOString(), + type: transaction.transaction.type, + gasUsed: transaction.transaction.gas_used, + status: transaction.transaction.status, + error: transaction.transaction.error, + owner: { + identifier: transaction.transaction.owner, + aliases: [{ + alias: identityAlias.alias, + status: 'ok' + }] + } + })) + + assert.deepEqual(expectedTransactions, body.resultSet) + }) + it('should return empty set of transactions desc with owner and type filter', async () => { const owner = transactions[0].transaction.owner diff --git a/packages/frontend/src/app/api/content.md b/packages/frontend/src/app/api/content.md index 4b0291052..b0c60b5ce 100644 --- a/packages/frontend/src/app/api/content.md +++ b/packages/frontend/src/app/api/content.md @@ -431,9 +431,11 @@ Status can be either `SUCCESS` or `FAIL`. In case of error tx, message will appe * `owner` Identity identifier * `status` can be `SUCCESS`, `FAIL` or `ALL` * `filters` array of transactions types +* `min` number of min `gas_used` +* `max` number of max `gas_used` ``` -GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL +GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL&min=0&max=9999999 { pagination: { @@ -1371,8 +1373,8 @@ IDENTITY_CREATE with instantLock "signature": "2019d90a905092dd3074da3cd42b05abe944d857fc2573e81e1d39a16ba659c00c7b38b88bee46a853c5c30deb9c2ae3abf4fbb781eec12b86a0928ca7b02ced7d", "documentTypeName": "domain", "indexName": "parentNameAndLabel", - "proTxHash": 'ad4e38fc81da72d61b14238ee6e5b91915554e24d725718800692d3a863c910b', "choice": "Abstain", + "proTxHash": 'ad4e38fc81da72d61b14238ee6e5b91915554e24d725718800692d3a863c910b', "raw": "08005b246080ba64350685fe302d3d790f5bb238cb619920d46230c844f079944a233bb2df460e72e3d59e7fe1c082ab3a5bd9445dd0dd5c4894a6d9f0d9ed9404b5000000e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c5315506646f6d61696e12706172656e744e616d65416e644c6162656c021204646173681203793031010c00412019d90a905092dd3074da3cd42b05abe944d857fc2573e81e1d39a16ba659c00c7b38b88bee46a853c5c30deb9c2ae3abf4fbb781eec12b86a0928ca7b02ced7d" } ``` From b2772f3f8daf189e4ca1db7087c6173a3b3aac35 Mon Sep 17 00:00:00 2001 From: owl352 Date: Fri, 29 Nov 2024 20:21:12 +0300 Subject: [PATCH 06/22] initial commit --- .github/workflows/build.yml | 7 +++++-- .github/workflows/deploy.yml | 7 ++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f53acf5f2..0073215c6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,7 @@ on: branches: - 'master' pull_request: - branches: [ "master" ] + branches: [ "master", "develop" ] env: REGISTRY: ghcr.io @@ -18,8 +18,11 @@ jobs: runs-on: ubuntu-latest steps: + - name: "Validate branch" + if: ${{github.base_ref != 'develop'}} + run: exit 1 - name: "Validate label" - if: | + if: | (contains(github.event.pull_request.labels.*.name, 'backend') || contains(github.event.pull_request.labels.*.name, 'data contract') || contains(github.event.pull_request.labels.*.name, 'frontend') || diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c405dea72..629120576 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,6 +6,7 @@ on: branches: - "master" - "*.*.*" + - "develop" types: - completed @@ -36,11 +37,11 @@ jobs: docker rm pe-testnet-api pe-testnet-indexer || true drop_db: - runs-on: ubuntu-latest + runs-on: ubuntu-latest - needs: stop + needs: stop - steps: + steps: - name: Checkout repository uses: actions/checkout@v4 with: From e273e0fad7cbc0a5fc8c1ebc1489781921959213 Mon Sep 17 00:00:00 2001 From: owl352 Date: Fri, 29 Nov 2024 20:27:28 +0300 Subject: [PATCH 07/22] base change --- .github/workflows/deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 629120576..b2cca3eec 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,5 +1,6 @@ name: "Deploy" + on: workflow_run: workflows: ["Build and push packages"] From 83aeb8aa26a09d80f3fc07faf79c6b5c5bf080b5 Mon Sep 17 00:00:00 2001 From: owl352 Date: Fri, 29 Nov 2024 20:27:37 +0300 Subject: [PATCH 08/22] base change --- .github/workflows/deploy.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b2cca3eec..629120576 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,6 +1,5 @@ name: "Deploy" - on: workflow_run: workflows: ["Build and push packages"] From 8d71ead7c585ed862cd1ac0d5560585be97917b1 Mon Sep 17 00:00:00 2001 From: owl352 Date: Fri, 29 Nov 2024 21:13:09 +0300 Subject: [PATCH 09/22] initial commit --- packages/api/README.md | 619 +++++++++++++++-- .../src/controllers/ValidatorsController.js | 8 + packages/api/src/routes.js | 35 +- .../api/test/integration/validators.spec.js | 102 ++- packages/frontend/src/app/api/content.md | 624 ++++++++++++++++-- 5 files changed, 1296 insertions(+), 92 deletions(-) diff --git a/packages/api/README.md b/packages/api/README.md index 17f884fcd..7ba885d9b 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -48,6 +48,7 @@ Reference: * [Blocks](#blocks) * [Validators](#validators) * [Validator by ProTxHash](#validator-by-protxhash) +* [Validator by Identity](#validator-by-identity) * [Validator Blocks Statistic](#validator-stats-by-protxhash) * [Validator Rewards Statistic](#validator-rewards-stats-by-protxhash) * [Transaction by hash](#transaction-by-hash) @@ -58,6 +59,7 @@ Reference: * [Documents by Data Contract](#documents-by-data-contract) * [Identity by Identifier](#identity-by-identifier) * [Identity by DPNS](#identity-by-dpns) +* [Identity Withdrawals](#identity-withdrawals) * [Identities](#identities) * [Data Contracts by Identity](#data-contracts-by-identity) * [Documents by Identity](#documents-by-identity) @@ -65,6 +67,7 @@ Reference: * [Transfers by Identity](#transfers-by-identity) * [Transactions history](#transactions-history) * [Rate](#rate) +* [Decode Raw Transaction](#decode-raw-transaction) ### Status Returns basic stats and epoch info @@ -300,6 +303,89 @@ Get validator by ProTxHash. ``` GET /validator/F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0 +{ + proTxHash: "F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0", + isActive: true, + proposedBlocksAmount: 5, + lastProposedBlockHeader: { + height: 5, + timestamp: "2024-06-23T13:51:44.154Z", + hash: "7253F441FF6AEAC847F9E03672B9386E35FC8CBCFC4A7CC67557FCA10E342904", + l1LockedHeight: 1337, + appVersion: 1, + blockVersion: 13, + validator: "F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0" + }, + proTxInfo: { + type: "Evo", + collateralHash: "6ce8545e25d4f03aba1527062d9583ae01827c65b234bd979aca5954c6ae3a59", + collateralIndex: 19, + collateralAddress: "yYK3Kiq36Xmf1ButkTUYb1iCNtJfSSM4KH", + operatorReward: 0, + confirmations: 214424, + state: { + version: 2, + service: "35.164.23.245:19999", + registeredHeight: 850334, + lastPaidHeight: 1064721, + consecutivePayments: 0, + PoSePenalty: 0, + PoSeRevivedHeight: 1027671, + PoSeBanHeight: -1, + revocationReason: 0, + ownerAddress: "yWrbg8HNwkogZfqKe1VW8czS9KiqdjvJtE", + votingAddress: "yWrbg8HNwkogZfqKe1VW8czS9KiqdjvJtE", + platformNodeID: "b5f25f8f70cf8d05c2d2970bdf186c994431d84e", + platformP2PPort: 36656, + platformHTTPPort: 1443, + payoutAddress: "yeRZBWYfeNE4yVUHV4ZLs83Ppn9aMRH57A", + pubKeyOperator: "b928fa4e127214ccb2b5de1660b5e371d2f3c9845077bc3900fc6aabe82ddd2e61530be3765cea15752e30fc761ab730", + } + }, + identity: "8tsWRSwsTM5AXv4ViCF9gu39kzjbtfFDM6rCyL2RcFzd", + identityBalance: 0, + epochInfo: { + number: 1982, + firstBlockHeight: 31976, + firstCoreBlockHeight: 1118131, + startTime: 1728488466559, + feeMultiplier: 1, + endTime: 1728492066559 + }, + totalReward: 0, + epochReward: 0, + withdrawalsCount: 1, + lastWithdrawal: "01FE1F00379C66C6E3BFD81A088E57E17613EC36E4FF812458535A8ABCB84047", + lastWithdrawalTime: "2024-10-12T03:15:19.257Z", + endpoints: { + coreP2PPortStatus: { + host: '52.33.28.41', + port: 19999, + status: 'ERROR', + message: null + }, + platformP2PPortStatus: { + host: '52.33.28.41', + port: 36656, + status: 'ERROR', + message: null + }, + platformGrpcPortStatus: { + host: '52.33.28.41', + port: 1443, + status: 'ERROR', + message: null + } + } +} +``` +--- +### Validator by Identity +Get validator by Identity. +* `lastProposedBlockHeader` field is nullable +``` +GET /validator/identity/8tsWRSwsTM5AXv4ViCF9gu39kzjbtfFDM6rCyL2RcFzd + { proTxHash: "F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0", isActive: true, @@ -716,53 +802,38 @@ Response codes: ### Identity Withdrawals Return all withdrawals for identity -_Note: this request does not contain any pagination data in the response_ +_Note: this request does not contain any pagination data in the response_ * `limit` cannot be more then 100 +* returns 404 `not found` if identity don't have withdrawals +* Pagination always `null` ``` GET /identity/A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb/withdrawals?limit=5 -[ - { - "timestamp": 1729096625509, - "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", - "id": "95eiiqMotMvH23f6cv3BPC4ykcHFWTy2g3baCTWZANAs", - "amount": 200000, - "status": 3 - }, - { - "timestamp": 1729096140465, - "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", - "id": "DJzb8nj7JTHwnvAGEGhyFc5hHLFa5Es9WFAyS4HhhNeF", - "amount": 200000, - "status": 3 - }, - { - "timestamp": 1729096636318, - "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", - "id": "E4gbWCQgqrz9DVrzCeDKhr4PVsfp6CeL5DUAYndRVWdk", - "amount": 200000, - "status": 3 - }, - { - "timestamp": 1729096795042, - "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", - "id": "FouX2qY8Eaxj5rSBrH9uxbhAM16ozrUP4sJwdo9pL7Cr", - "amount": 200000, - "status": 3 +{ + pagination: { + limit: null, + page: null, + total: null }, - { - "timestamp": 1729097247874, - "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", - "id": "9VEpb2aJRnCxfi3LjFXWa1zshkBPfzzHHh5yqEkgqw1t", - "amount": 200000, - "status": 3 - } -] + resultSet: [ + { + "document": "95eiiqMotMvH23f6cv3BPC4ykcHFWTy2g3baCTWZANAs", + "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", + "status": "COMPLETE", + "amount": 200000, + "timestamp": 1729096625509, + "withdrawalAddress": "yeRZBWYfeNE4yVUHV4ZLs83Ppn9aMRH57A", + "hash": "113F86F4D1F48159B0D6690F3C5F8F33E39243086C041CF016454A66AD63F025" + }, + ... + ] +} ``` Response codes: ``` 200: OK +404: Not Found 500: Internal Server Error ``` --- @@ -951,3 +1022,477 @@ Response codes: 500: Internal Server Error 503: Service Temporarily Unavailable ``` +### Decode Raw Transaction +Return a decoded State Transition + +Available transactions type for decode + +| Transition type | type index | +|------------------------------|------------| +| `DATA_CONTRACT_CREATE` | 0 | +| `DOCUMENTS_BATCH` | 1 | +| `IDENTITY_CREATE` | 2 | +| `IDENTITY_TOP_UP` | 3 | +| `DATA_CONTRACT_UPDATE` | 4 | +| `IDENTITY_UPDATE` | 5 | +| `IDENTITY_CREDIT_WITHDRAWAL` | 6 | +| `IDENTITY_CREDIT_TRANSFER` | 7 | +| `MASTERNODE_VOTE` | 8 | + +- `fundingAddress` can be null +- `prefundedBalance` can be null +- `contractBounds` always null + +``` +POST /transaction/decode + +{ + "base64": "AAAA56Y/VzBp5vlrJR8JRCPSDLlaZjngwyM50w8dQAmAe3EAAAAAAAEBAAABYpzp8+tOQ8j6k24W7FXjqo7zZmMZcybMIDLw7VfLT0EAAQZsYWJsZXIWBBIEdHlwZRIGb2JqZWN0Egpwcm9wZXJ0aWVzFgISCmNvbnRyYWN0SWQWBBIEdHlwZRIGc3RyaW5nEgltaW5MZW5ndGgDVhIJbWF4TGVuZ3RoA1gSCHBvc2l0aW9uAwASCXNob3J0TmFtZRYEEgR0eXBlEgZzdHJpbmcSCW1heExlbmd0aANAEgltaW5MZW5ndGgDBhIIcG9zaXRpb24DAhIIcmVxdWlyZWQVAhIJc2hvcnROYW1lEgpjb250cmFjdElkEhRhZGRpdGlvbmFsUHJvcGVydGllcxMACgACQR8AOrSAQ3S/emVWILS8WyHcMA97CtY5rH7dB4DSjAm/0x6DZdZcm8jyGIdIuuTUALR8/N724YhxwhOQHqUm5ipN" +} +``` +#### Responses: +``` +{ + "type": 0, + "internalConfig": { + "canBeDeleted": false, + "readonly": false, + "keepsHistory": false, + "documentsKeepHistoryContractDefault": false, + "documentsMutableContractDefault": true, + "documentsCanBeDeletedContractDefault": true, + "requiresIdentityDecryptionBoundedKey": null, + "requiresIdentityEncryptionBoundedKey": null + }, + "userFeeIncrease": 0, + "identityNonce": 7, + "dataContractId": "4PenkX3rPnwvBNvCwRTXaduxym7XG4yJWQNfiruWwM2N", + "ownerId": "7Yowk46VwwHqmD5yZyyygggh937aP6h2UW7aQWBdWpM5", + "schema": { + "identityVerify": { + "documentsMutable": true, + "canBeDeleted": true, + "type": "object", + "properties": { + "normalizedLabel": { + "position": 0, + "type": "string", + "pattern": "^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-]{0,61}[a-hj-km-np-z0-9]$", + "maxLength": 63, + "description": "Domain label converted to lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'b0b'", + "$comment": "Must match a domain document to provide further information. Must be equal to the label in lowercase. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\"." + }, + "normalizedParentDomainName": { + "type": "string", + "pattern": "^$|^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-\\.]{0,61}[a-hj-km-np-z0-9]$", + "minLength": 0, + "maxLength": 63, + "position": 1, + "description": "A parent domain name in lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'dash'", + "$comment": "Must either be equal to an existing domain or empty to create a top level domain. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\". Only the data contract owner can create top level domains." + }, + "url": { + "position": 2, + "type": "string", + "description": "The identity verification URL to be stored.", + "maxLength": 128, + "pattern": "^https?://.*", + "format": "uri" + } + }, + "indices": [ + { + "name": "ownerId", + "properties": [ + { + "$ownerId": "asc" + } + ] + }, + { + "name": "ownerId_NormDomainName_NormLabel", + "properties": [ + { + "$ownerId": "asc" + }, + { + "normalizedParentDomainName": "asc" + }, + { + "normalizedLabel": "asc" + } + ] + }, + { + "name": "uniqueUsernameIndex", + "properties": [ + { + "normalizedLabel": "asc" + } + ] + } + ], + "required": [ + "url", + "normalizedLabel", + "normalizedParentDomainName" + ], + "additionalProperties": false + }, + "tx_metadata": { + "type": "object", + "indices": [ + { + "name": "ownerId", + "properties": [ + { + "$ownerId": "asc" + } + ] + }, + { + "name": "ownerIdAndCreatedAt", + "properties": [ + { + "$ownerId": "asc" + }, + { + "$createdAt": "asc" + } + ] + } + ], + "properties": { + "keyIndex": { + "type": "integer", + "minimum": 0, + "description": "The index of the owners identity public key used to derive the encryption key.", + "position": 0 + }, + "encryptionKeyIndex": { + "type": "integer", + "minimum": 0, + "description": "The secondary index used to derive the encryption key that is used to encrypt and decrypt encryptedData.", + "position": 1 + }, + "encryptedMetadata": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 4096, + "description": "encrypted metadata using AES-CBC-256", + "position": 2 + } + }, + "required": [ + "keyIndex", + "encryptionKeyIndex", + "encryptedMetadata", + "$createdAt" + ], + "additionalProperties": false + } + }, + "signature": "1ff784ded3a1177d0c6194fadfaf3a3722cb4ef41c1ae3fa7bdeca2f0bd5a19cdc7d95d2286990e4e8a9308043261c4ed7fdb5676d4d5629ef26d26a79a19a0f3a", + "signaturePublicKeyId": 1, + "raw": "00000032609106c20cd642005b3c1ed00dde5cb5fde5f40baaa4184aebbafc560b679f00000000000101000001614c34c98bc3f0a618951f0e61310598a0ad8ec225007f1f001fddf7e7b292f000020e6964656e7469747956657269667916071210646f63756d656e74734d757461626c651301120c63616e426544656c65746564130112047479706512066f626a656374120a70726f706572746965731603120f6e6f726d616c697a65644c6162656c16061208706f736974696f6e02001204747970651206737472696e6712077061747465726e123c5e5b612d686a2d6b6d2d6e702d7a302d395d5b612d686a2d6b6d2d6e702d7a302d392d5d7b302c36317d5b612d686a2d6b6d2d6e702d7a302d395d2412096d61784c656e677468023f120b6465736372697074696f6e12a3446f6d61696e206c6162656c20636f6e76657274656420746f206c6f7765726361736520666f7220636173652d696e73656e73697469766520756e697175656e6573732076616c69646174696f6e2e20226f222c2022692220616e6420226c22207265706c6163656420776974682022302220616e642022312220746f206d6974696761746520686f6d6f67726170682061747461636b2e20652e672e202762306227120824636f6d6d656e7412994d757374206d61746368206120646f6d61696e20646f63756d656e7420746f2070726f76696465206675727468657220696e666f726d6174696f6e2e204d75737420626520657175616c20746f20746865206c6162656c20696e206c6f776572636173652e20226f222c2022692220616e6420226c22206d757374206265207265706c6163656420776974682022302220616e64202231222e121a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6516071204747970651206737472696e6712077061747465726e12415e247c5e5b612d686a2d6b6d2d6e702d7a302d395d5b612d686a2d6b6d2d6e702d7a302d392d5c2e5d7b302c36317d5b612d686a2d6b6d2d6e702d7a302d395d2412096d696e4c656e677468020012096d61784c656e677468023f1208706f736974696f6e0201120b6465736372697074696f6e12a24120706172656e7420646f6d61696e206e616d6520696e206c6f7765726361736520666f7220636173652d696e73656e73697469766520756e697175656e6573732076616c69646174696f6e2e20226f222c2022692220616e6420226c22207265706c6163656420776974682022302220616e642022312220746f206d6974696761746520686f6d6f67726170682061747461636b2e20652e672e20276461736827120824636f6d6d656e7412c04d7573742065697468657220626520657175616c20746f20616e206578697374696e6720646f6d61696e206f7220656d70747920746f20637265617465206120746f70206c6576656c20646f6d61696e2e20226f222c2022692220616e6420226c22206d757374206265207265706c6163656420776974682022302220616e64202231222e204f6e6c7920746865206461746120636f6e7472616374206f776e65722063616e2063726561746520746f70206c6576656c20646f6d61696e732e120375726c16061208706f736974696f6e02021204747970651206737472696e67120b6465736372697074696f6e122b546865206964656e7469747920766572696669636174696f6e2055524c20746f2062652073746f7265642e12096d61784c656e677468028012077061747465726e120c5e68747470733f3a2f2f2e2a1206666f726d617412037572691207696e64696365731503160212046e616d6512076f776e65724964120a70726f70657274696573150116011208246f776e657249641203617363160212046e616d6512206f776e657249645f4e6f726d446f6d61696e4e616d655f4e6f726d4c6162656c120a70726f70657274696573150316011208246f776e6572496412036173631601121a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6512036173631601120f6e6f726d616c697a65644c6162656c1203617363160212046e616d651213756e69717565557365726e616d65496e646578120a70726f7065727469657315011601120f6e6f726d616c697a65644c6162656c1203617363120872657175697265641503120375726c120f6e6f726d616c697a65644c6162656c121a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6512146164646974696f6e616c50726f7065727469657313000b74785f6d65746164617461160512047479706512066f626a6563741207696e64696365731502160212046e616d6512076f776e65724964120a70726f70657274696573150116011208246f776e657249641203617363160212046e616d6512136f776e65724964416e64437265617465644174120a70726f70657274696573150216011208246f776e6572496412036173631601120a246372656174656441741203617363120a70726f70657274696573160312086b6579496e64657816041204747970651207696e746567657212076d696e696d756d0200120b6465736372697074696f6e124e54686520696e646578206f6620746865206f776e657273206964656e74697479207075626c6963206b6579207573656420746f206465726976652074686520656e6372797074696f6e206b65792e1208706f736974696f6e02001212656e6372797074696f6e4b6579496e64657816041204747970651207696e746567657212076d696e696d756d0200120b6465736372697074696f6e1268546865207365636f6e6461727920696e646578207573656420746f206465726976652074686520656e6372797074696f6e206b65792074686174206973207573656420746f20656e637279707420616e64206465637279707420656e63727970746564446174612e1208706f736974696f6e02011211656e637279707465644d657461646174611606120474797065120561727261791209627974654172726179130112086d696e4974656d73022012086d61784974656d7302fb1000120b6465736372697074696f6e1224656e63727970746564206d65746164617461207573696e67204145532d4342432d3235361208706f736974696f6e020212087265717569726564150412086b6579496e6465781212656e6372797074696f6e4b6579496e6465781211656e637279707465644d65746164617461120a2463726561746564417412146164646974696f6e616c50726f706572746965731300070001411ff784ded3a1177d0c6194fadfaf3a3722cb4ef41c1ae3fa7bdeca2f0bd5a19cdc7d95d2286990e4e8a9308043261c4ed7fdb5676d4d5629ef26d26a79a19a0f3a" +} +``` +``` +{ + "type": 1, + "transitions": [ + { + "id": "Fwvkm4ktZVJEHN34RWm4s3fbNXoC3vNj9MB49LwLdavM", + "dataContractId": "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec", + "revision": 1, + "type": "domain", + "action": 0, + "data": { + "label": "Microsoft", + "normalizedLabel": "m1cr0s0ft", + "normalizedParentDomainName": "dash", + "parentDomainName": "dash", + "preorderSalt": "+nE4I9leD/ua1WdxrrZ4gWQWglNUPwRRjgULjvqGOCs=", + "records": { + "identity": "HVfqSPfdmiHsrajx7EmErGnV597uYdH3JGhvwpVDcdAT" + }, + "subdomainRules": { + "allowSubdomains": false + } + }, + "prefundedBalance": { + "parentNameAndLabel": 20000000000 + } + } + ], + "userFeeIncrease": 0, + "signature": "2056f7e8fafc7328ed3026466f90cc0d50ad04714c30d3bc4dc7228440adc2b7974091f27b51dc23de470b5e235741c2a852f74c8ab73d7e83469fe24dcffbe425", + "signaturePublicKeyId": 2, + "ownerId": "HVfqSPfdmiHsrajx7EmErGnV597uYdH3JGhvwpVDcdAT", + "raw": "0200f5132a1cc9642bdab3d06533a91a4f8813129c9a27a73ed53e577e747c9a4eac01000000de15c2d39a27cbe01395094153e58ded53c7cf9fabfe6723810b56e6ee35f50a0706646f6d61696ee668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155a1a2c90b886fe21aa4d646b03e0d2775df9a16f06157130247dd435334fc051f07056c6162656c12094d6963726f736f66740f6e6f726d616c697a65644c6162656c12096d31637230733066741a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6512046461736810706172656e74446f6d61696e4e616d651204646173680c7072656f7264657253616c740cfa713823d95e0ffb9ad56771aeb6788164168253543f04518e050b8efa86382b077265636f726473160112086964656e7469747910f5132a1cc9642bdab3d06533a91a4f8813129c9a27a73ed53e577e747c9a4eac0e737562646f6d61696e52756c65731601120f616c6c6f77537562646f6d61696e7313000112706172656e744e616d65416e644c6162656cfd00000004a817c8000002412056f7e8fafc7328ed3026466f90cc0d50ad04714c30d3bc4dc7228440adc2b7974091f27b51dc23de470b5e235741c2a852f74c8ab73d7e83469fe24dcffbe425" +} +``` +``` +IDENTITY_CREATE with chainLock + +{ + "type": 2, + "fundingAddress": null, + "assetLockProof": { + coreChainLockedHeight: 1138871, + type: "chainLock", + fundingAmount: null, + txid: "fc89dd4cbe2518da3cd9737043603e81665df58d4989a38b2942eec56bacad1d", + vout: 0, + fundingAddress: null, + instantLock: null + }, + "userFeeIncrease": 0, + "identityId": "awCc9STLEgw8pJozBq24QzGK5Th9mow7V3EjjY9M7aG", + "signature": "2015bf50b422df6ccfdc4bcdcae76a11106c8f6c627a284f37d2591184e413249350a722926c8899b5514fd94603598031358bc2a0ac031fb402ecc5b8025f2141", + "raw": "0300010000020000000014b23f76cac0218f7637c924e45212bb260cff29250001fc001160b72021007051d2207c45d7592bb7e3e5b4b006a29cfe1899aea8abf00c50ee8a40860000412015bf50b422df6ccfdc4bcdcae76a11106c8f6c627a284f37d2591184e413249350a722926c8899b5514fd94603598031358bc2a0ac031fb402ecc5b8025f214108b1737186062205ee3a5f7e19454121b648e0806c7bc1e8bc073c38217a28e1", + "publicKeys": [ + { + contractBounds: null, + id: 0, + type: "ECDSA_SECP256K1", + data: "0348a6a633850f3c83a0cb30a9fceebbaa3b9ab3f923f123d92728cef234176dc5", + publicKeyHash: "07630dddc55729c043de7bdeb145ee0d44feae3b", + purpose: "AUTHENTICATION", + securityLevel: "MASTER", + readOnly: false, + signature: "2042186a3dec52bfe9a24ee17b98adc5efcbc0a0a6bacbc9627f1405ea5e1bb7ae2bb94a270363400969669e9884ab9967659e9a0d8de7464ee7c47552c8cb0e99" + }, + { + contractBounds: null, + id: 1, + type: "ECDSA_SECP256K1", + data: "034278b0d7f5e6d902ec5a30ae5c656937a0323bdc813e851eb8a2d6a1d23c51cf", + publicKeyHash: "e2615c5ef3f910ebe5ada7930e7b2c04a7ffbb23", + purpose: "AUTHENTICATION", + securityLevel: "HIGH", + readOnly: false, + signature: "1fbb0d0bb63d26c0d5b6e1f4b8c0eebef4d256c4e8aa933a2cb6bd6b2d8aae545215312924c7dd41c963071e2ccfe2187a8684d93c55063cb45fdd03e76344d6a4" + }, + { + contractBounds: null, + id: 2, + type: "ECDSA_SECP256K1", + data: "0245c3b0f0323ddbb9ddf123f939bf37296af4f38fa489aad722c50486575cd8f4", + publicKeyHash: "d53ee3b3518fee80816ab26af98a34ea60ae9af7", + purpose: "AUTHENTICATION", + securityLevel: "CRITICAL", + readOnly: false, + signature: "204013dcca13378b820e40cf1da77abe38662546ef0a304545de3c35845b83a7ad4b42051c2b3539c9181b3f0cb3fb4bc970db89663c6bd6ca1468568a62beaa75" + } + ] +} +``` +``` +IDENTITY_CREATE with instantLock + +{ + "type": 2, + "fundingAddress": "yV1ZYoep5FFSBxKWM24JUwKfnAkFHnXXV7", + "assetLockProof": { + coreChainLockedHeight: null + type: "instantSend", + fundingAmount: 34999000, + txid: "fc89dd4cbe2518da3cd9737043603e81665df58d4989a38b2942eec56bacad1d", + vout: 0, + fundingAddress: "yeMdYXBPum8RmHvrq5SsYE9zNYhMEimbUY", + instantLock: 'AQEKM9t1ICNzvddKryjM4enKn0Y5amBn3o6DwDoC4uk5SAAAAAAdraxrxe5CKYujiUmN9V1mgT5gQ3Bz2TzaGCW+TN2J/JQP49yOk0uJ6el6ls9CmNo++yPYoX1Sx1lWEZTTAAAAhXiuCBXgzawuboxMAXDiXQpJCCPi417VE4mdcYPgTa0/Hd+RCHLAR6H+MXhqKazlGddI7AdWxxLZ94ZvQu+qIpe7G9XRRjQWeYwroIyc6MqQF5mKpvV0AUMYUNMXjCsq' + }, + "userFeeIncrease": 0, + "identityId": "BHAuKDRVPHkJd99pLoQh8dfjUFobwk5bq6enubEBKpsv", + "signature": "1fc5b49ce2feb6cfc94f31de8167b806af0265657d5b8f01584e0db3ca011dba24328998bf40a50dd06b6ab10ed47622f46c07dec4d7cad3625b41aa52c9e11c2f", + "raw": "03000400000000000000210258abe04886308feb52b8f3d64eace4913c9d049f4dda9a88a217e6ca6b89a107411f60451588fe72a067daaa0a1ee04279e77ce346128560129162386f76d51eccdc1d88704f2262fe173de57e5598010655d410da94ae2e1cf7086049878b08e966000100000200002103e6680bb560e40adb299a6b940d3dcbe2b08e6e1a64bc8f6bc9ec3d638655c3554120066559ccd6cea8ac2d366980f77a94cbfdfbd978803edbf4066f42bc53adcdb51956fb0d3c9cec2012583d17b66456094a8620109d6dae29dc562b2870592940000200000100002102326a8c19a1c58d30d142e113d0ddf381d95a6306f56c9ec4d3cb8c4823685b29411f5bb82721b58d92e67db9fb699ab939ccc4a6d5e2e498e80dfb8a3535c13f571923f045e645a898762f8305a4a2218bfedb060f8a8492c48ae9c96247ce17710b00030003010000210252a2d08f295871ec4b04cb0bcf7b2899b0b004702d9058982dd797141d527e78412044820dc7651186634326922eda85741bb3f9f005057d94b36845a7edc16ed1df4d5ccabd7e7f003e9c189847fbc06e943252640bc47963c42ae6c0d87b7b506b00c601014fae5c4ed0e736dd25610b65ff51b56cbe1b9467520f0ced40a9e3b58e4555b10100000077ba9d450b94520434c5d15339922aa7df81b51344b98588649a44752f1a355cd0d05ce3df52f3fb7fc6e64cc414fb7cd9d0ffc4d088e77d9e542fade30000008a8678665212af134cfa36ea40984009adca338efa3131667f5a62b489d2fb2713eb7eccd14dd83cc6679b597548feae18bdc393dae2ab2a50844220359d4b87c428507808dc8df62f56dabb8d1eae2c1859b9ca54b3b4820ebc8453f57c34f6ef03000800014fae5c4ed0e736dd25610b65ff51b56cbe1b9467520f0ced40a9e3b58e4555b1010000006a473044022070293df3b93c523373f1f86272c5dba7886ab41cfc50b0b89658c07d0825c16002201afdf3b31393c5b99373597042b4d651028e824fc12f802aa1be51cc165bcf1e012103d55244573359ad586597b9bb4dd31b8f145121b7c01146656bc26c4b99184a47ffffffff0240420f0000000000026a0049ac4c2e000000001976a91441bb9b42b9f0d589008b4a7f6a72a6bb342b386d88ac0000000024010140420f00000000001976a9145f573cd6a8570cb0b74c4b0ea15334e6bd6b34a788ac0000411fc5b49ce2feb6cfc94f31de8167b806af0265657d5b8f01584e0db3ca011dba24328998bf40a50dd06b6ab10ed47622f46c07dec4d7cad3625b41aa52c9e11c2f98b95bbff1488807c3a4ed36c5fde32f9a6f1e05a622938476652041669e4135", + "publicKeys": [ + { + contractBounds: null, + id: 0, + type: "ECDSA_SECP256K1", + data: "0348a6a633850f3c83a0cb30a9fceebbaa3b9ab3f923f123d92728cef234176dc5", + publicKeyHash: "07630dddc55729c043de7bdeb145ee0d44feae3b", + purpose: "AUTHENTICATION", + securityLevel: "MASTER", + readOnly: false, + signature: "2042186a3dec52bfe9a24ee17b98adc5efcbc0a0a6bacbc9627f1405ea5e1bb7ae2bb94a270363400969669e9884ab9967659e9a0d8de7464ee7c47552c8cb0e99" + }, + { + contractBounds: null, + id: 1, + type: "ECDSA_SECP256K1", + data: "034278b0d7f5e6d902ec5a30ae5c656937a0323bdc813e851eb8a2d6a1d23c51cf", + publicKeyHash: "e2615c5ef3f910ebe5ada7930e7b2c04a7ffbb23", + purpose: "AUTHENTICATION", + securityLevel: "HIGH", + readOnly: false, + signature: "1fbb0d0bb63d26c0d5b6e1f4b8c0eebef4d256c4e8aa933a2cb6bd6b2d8aae545215312924c7dd41c963071e2ccfe2187a8684d93c55063cb45fdd03e76344d6a4" + }, + { + contractBounds: null, + id: 2, + type: "ECDSA_SECP256K1", + data: "0245c3b0f0323ddbb9ddf123f939bf37296af4f38fa489aad722c50486575cd8f4", + publicKeyHash: "d53ee3b3518fee80816ab26af98a34ea60ae9af7", + purpose: "AUTHENTICATION", + securityLevel: "CRITICAL", + readOnly: false, + signature: "204013dcca13378b820e40cf1da77abe38662546ef0a304545de3c35845b83a7ad4b42051c2b3539c9181b3f0cb3fb4bc970db89663c6bd6ca1468568a62beaa75" + } + ] +} +``` +``` +{ + type: 3, + assetLockProof: { + coreChainLockedHeight: null + type: "instantSend", + fundingAmount: 999000, + txid: "7734f498c5b59f64f73070e0a5ec4fa113065da00358223cf888c3c27317ea64", + vout: 0, + fundingAddress: "yWxCwVRgqRmePNPJxezgus1T7xSv5q17SU" + }, + identityId: '4EfA9Jrvv3nnCFdSf7fad59851iiTRZ6Wcu6YVJ4iSeF', + amount: 300000000, + signature: '810cd0bfe02104362941d35bd05fdf82cdc50c3bc8510077bfa62d47b68710', + raw: '040000c60101ecd6b031477f342806df5740b70f93b8a3e925bbf2d90d979a5ed162a8d7d5660000000064ea1773c2c388f83c225803a05d0613a14feca5e07030f7649fb5c598f43477940fe3dc8e934b89e9e97a96cf4298da3efb23d8a17d52c759561194d3000000a5e81597e94558618bf1464801188ecbc09c7a12e73489225c63684259f075f87aa3d47ea9bbbe1f9c314086ddc35a6d18b30ff4fe579855779f9268b8bf5c79760c7d8c56d34163931f016c2e3036852dd33a6b643dd59dc8c54199f34e3d2def0300080001ecd6b031477f342806df5740b70f93b8a3e925bbf2d90d979a5ed162a8d7d566000000006a4730440220339d4d894eb2ff9c193bd8c33cdb3030a8be18ddbf30d983e8286c08c6c4c7d90220181741d9eed3814ec077030c26c0b9fff63b9ef10e1e6ca1c87069b261b0127a0121034951bbd5d0d500942426507d4b84e6d88406300ed82009a8db087f493017786affffffff02e093040000000000026a0078aa0a00000000001976a914706db5d1e8fb5f925c6db64104f4b77f0c8b73d488ac00000000240101e0930400000000001976a91474a509b4f3b80ce818465dc0f9f66e2103d9178b88ac003012c19b98ec0033addb36cd64b7f510670f2a351a4304b5f6994144286efdac411f810cd0bfe02104362941d35bd05fdf82cdc50c3bc8510077bfa62d47b68710' +} +``` +``` +{ + "type": 4, + "internalConfig": { + "canBeDeleted": false, + "readonly": false, + "keepsHistory": false, + "documentsKeepHistoryContractDefault": false, + "documentsMutableContractDefault": true, + "documentsCanBeDeletedContractDefault": true, + "requiresIdentityDecryptionBoundedKey": null, + "requiresIdentityEncryptionBoundedKey": null + }, + "identityContractNonce": 6, + "signaturePublicKeyId": 2, + "signature": "1ff9a776c62ee371a0e5ed95e8efe27c7955f247d5527670e43cbd837e73cfaef3613592b9798e9afd2526e3b92330f07d0c5f1396390d63ad39b4bebeb9c82903", + "userFeeIncrease": 0, + "ownerId": "GgZekwh38XcWQTyWWWvmw6CEYFnLU7yiZFPWZEjqKHit", + "dataContractId": "AJqYb8ZvfbA6ZFgpsvLfpMEzwjaYUPyVmeFxSJrafB18", + "dataContractNonce": 0, + "schema": { + "note": { + "type": "object", + "properties": { + "message": { + "type": "string", + "position": 0 + }, + "author": { + "type": "string", + "position": 1 + } + }, + "additionalProperties": false + } + }, + "version": 2, + "dataContractOwner": "GgZekwh38XcWQTyWWWvmw6CEYFnLU7yiZFPWZEjqKHit", + "raw": "010006008a4af217f340e9c4c95857496cf33b68eb6c712ac6d20a1eb7854d14afd9ffcf00000000000101000002e901dfc172a96ce3f7d334d6c0b69df3b01c86d30ff03a7c24f516838f94340d0001046e6f7465160312047479706512066f626a656374120a70726f70657274696573160212076d65737361676516021204747970651206737472696e671208706f736974696f6e02001206617574686f7216021204747970651206737472696e671208706f736974696f6e020112146164646974696f6e616c50726f7065727469657313000002411ff9a776c62ee371a0e5ed95e8efe27c7955f247d5527670e43cbd837e73cfaef3613592b9798e9afd2526e3b92330f07d0c5f1396390d63ad39b4bebeb9c82903" +} +``` +``` +{ + type: 5, + identityContractNonce: 3, + userFeeIncrease: 0, + identityId: '4NGALjtX2t3AXE3ZCqJiSmYuiWEY3ZPQNUBxNWWRrRSp', + revision: 2, + publicKeysToAdd: [ + { + contractBounds: null, + id: 5, + type: "ECDSA_HASH160", + data: "c208ded6d1af562b8e5387c02a446ea6e8bb325f", + publicKeyHash: "c208ded6d1af562b8e5387c02a446ea6e8bb325f", + purpose: "AUTHENTICATION", + securityLevel: "HIGH", + readOnly: false, + signature: "" + }, + { + contractBounds: null, + id: 6, + type: "ECDSA_SECP256K1", + data: "026213380930c93c4b53f6ddbc5adc5f5165102d8f92f7d9a495a8f9c6e61b30f0", + publicKeyHash: "d39eda042126256a372c388bd191532a7c9612ce", + purpose: "AUTHENTICATION", + securityLevel: "MASTER", + readOnly: false, + signature: "1faf8b0f16320d0f9e29c1db12ab0d3ec87974b19f6fc1189a988cd85503d79f844d3ff778678d7f4f3829891e8e8d0183456194d9fc76ed66e503154996eefe06" + } + ], + setPublicKeyIdsToDisable: [], + signature: '1f341c8eb7b890f416c7a970406dd37da078dab5f2c4aa8dd18375516933b234873127965dd72ee28b7392fcd87e28c4bfef890791b58fa9c34bce9e96d6536cb1', + signaturePublicKeyId: 0, + raw: '0600320566816f366803517a7eb44d331ccb0e442fab6396f3d6ac631b1069aae0410203020005020002000014c208ded6d1af562b8e5387c02a446ea6e8bb325f000006000000000021026213380930c93c4b53f6ddbc5adc5f5165102d8f92f7d9a495a8f9c6e61b30f0411faf8b0f16320d0f9e29c1db12ab0d3ec87974b19f6fc1189a988cd85503d79f844d3ff778678d7f4f3829891e8e8d0183456194d9fc76ed66e503154996eefe06000000411f341c8eb7b890f416c7a970406dd37da078dab5f2c4aa8dd18375516933b234873127965dd72ee28b7392fcd87e28c4bfef890791b58fa9c34bce9e96d6536cb1' +} +``` +``` +{ + "type": 6, + "outputAddress": "yifJkXaxe7oM1NgBDTaXnWa6kXZAazBfjk", + "userFeeIncrease": 0, + "identityContractNonce": 6, + "senderId": "8eTDkBhpQjHeqgbVeriwLeZr1tCa6yBGw76SckvD1cwc", + "amount": 200000, + "identityNonce": 6, + "outputScript": "76a914f51453a538d9a0a9fb3fe0f2948a0f80d9cf525a88ac", + "coreFeePerByte": 5, + "signature": "20cc6d48ed7341d47d6efbdad14ce0f471e67f75110acd56738b7c42c78a71d7da4fd870e1c77934239ea3a0ca0fd1145814b5165bd4ec76e87e774836c680b01b", + "signaturePublicKeyId": 3, + "pooling": "Standard", + "raw": "05017199f1f68404c86ecf60d9cb93aef318fa0f2b08e59ffd176bdef43154ffde6bfc00030d400500011976a914f51453a538d9a0a9fb3fe0f2948a0f80d9cf525a88ac0600034120cc6d48ed7341d47d6efbdad14ce0f471e67f75110acd56738b7c42c78a71d7da4fd870e1c77934239ea3a0ca0fd1145814b5165bd4ec76e87e774836c680b01b" +} +``` +``` +{ + "type": 7, + "identityContractNonce": 1, + "userFeeIncrease": 0, + "senderId": "24YEeZmpy1QNKronDT8enYWLXnfoxYK7hrHUdpWHxURg", + "recipientId": "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs", + "amount": 21856638, + "signaturePublicKeyId": 3, + "signature": "1f39c5c81434699df7924d68eba4326352ac97883688e3ec3ffed36746d6fb8c227d4a96a40fcd38673f80ed64ab8e3514cf81fe8be319774429071881d3c8b1f8", + "raw": "07000fc3bf4a26bff60f4f79a1f4b929ce4d4c5833d226c1c7f68758e71d7ae229db569fd4f616b3dedecbeef95352cf38f1fb04d232a0d20623bc195b0c3f721840fc014d817e010003411f39c5c81434699df7924d68eba4326352ac97883688e3ec3ffed36746d6fb8c227d4a96a40fcd38673f80ed64ab8e3514cf81fe8be319774429071881d3c8b1f8" +} +``` +``` +{ + "type": 8, + "contestedResourcesVotePoll": [ + "EgRkYXNo", + "EgN5MDE=" + ], + "contractId": "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec", + "modifiedDataIds": [ + "523FUhxg6WEvp24PfjqFAuHFYXW1gkoXdy8QywfriSse" + ], + "ownerId": "523FUhxg6WEvp24PfjqFAuHFYXW1gkoXdy8QywfriSse", + "signature": "2019d90a905092dd3074da3cd42b05abe944d857fc2573e81e1d39a16ba659c00c7b38b88bee46a853c5c30deb9c2ae3abf4fbb781eec12b86a0928ca7b02ced7d", + "documentTypeName": "domain", + "indexName": "parentNameAndLabel", + "choice": "Abstain", + "proTxHash": 'ad4e38fc81da72d61b14238ee6e5b91915554e24d725718800692d3a863c910b', + "raw": "08005b246080ba64350685fe302d3d790f5bb238cb619920d46230c844f079944a233bb2df460e72e3d59e7fe1c082ab3a5bd9445dd0dd5c4894a6d9f0d9ed9404b5000000e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c5315506646f6d61696e12706172656e744e616d65416e644c6162656c021204646173681203793031010c00412019d90a905092dd3074da3cd42b05abe944d857fc2573e81e1d39a16ba659c00c7b38b88bee46a853c5c30deb9c2ae3abf4fbb781eec12b86a0928ca7b02ced7d" +} +``` +Response codes: +``` +200: OK +500: Internal Server Error +503: Service Temporarily Unavailable +``` diff --git a/packages/api/src/controllers/ValidatorsController.js b/packages/api/src/controllers/ValidatorsController.js index 1cb8ffb68..f5d098e9e 100644 --- a/packages/api/src/controllers/ValidatorsController.js +++ b/packages/api/src/controllers/ValidatorsController.js @@ -81,6 +81,14 @@ class ValidatorsController { ) } + getValidatorByIdentifier = async (request, response) => { + const { identifier } = request.params + + const hash = Buffer.from(base58.decode(identifier)).toString('hex') + + await this.getValidatorByProTxHash({ ...request, params: { hash } }, response) + } + getValidators = async (request, response) => { const { page = 1, limit = 10, order = 'asc', isActive = undefined } = request.query diff --git a/packages/api/src/routes.js b/packages/api/src/routes.js index 91e5d7340..a6b6b5ac5 100644 --- a/packages/api/src/routes.js +++ b/packages/api/src/routes.js @@ -8,17 +8,17 @@ * @param validatorsController {ValidatorsController} */ module.exports = ({ - fastify, - mainController, - epochController, - blocksController, - transactionsController, - dataContractsController, - documentsController, - identitiesController, - validatorsController, - rateController -}) => { + fastify, + mainController, + epochController, + blocksController, + transactionsController, + dataContractsController, + documentsController, + identitiesController, + validatorsController, + rateController + }) => { const routes = [ { path: '/status', @@ -294,6 +294,19 @@ module.exports = ({ querystring: { $ref: 'paginationOptions#' } } }, + { + path: '/validator/identity/:identifier', + method: 'GET', + handler: validatorsController.getValidatorByIdentifier, + schema: { + params: { + type: 'object', + properties: { + identifier: { $ref: 'identifier#' } + } + } + } + }, { path: '/validator/:hash/stats', method: 'GET', diff --git a/packages/api/test/integration/validators.spec.js b/packages/api/test/integration/validators.spec.js index 4b644840c..b1588864d 100644 --- a/packages/api/test/integration/validators.spec.js +++ b/packages/api/test/integration/validators.spec.js @@ -158,10 +158,10 @@ describe('Validators routes', () => { type: IDENTITY_CREDIT_WITHDRAWAL, block_hash: blocks[i].hash, owner: base58.encode(Buffer.from(( - (i % 2) - ? inactiveValidators - : activeValidators)[0].pro_tx_hash, - 'hex')) + (i % 2) + ? inactiveValidators + : activeValidators)[0].pro_tx_hash, + 'hex')) } ) @@ -297,6 +297,100 @@ describe('Validators routes', () => { }) }) + describe('getValidatorByIdentity()', async () => { + it('should return inactive validator by identifier', async () => { + const [validator] = inactiveValidators + + const identifier = base58.encode(Buffer.from(validator.pro_tx_hash, 'hex')) + + const { body } = await client.get(`/validator/identity/${identifier}`) + .expect(200) + .expect('Content-Type', 'application/json; charset=utf-8') + + const expectedValidator = { + proTxHash: validator.pro_tx_hash, + isActive: false, + proposedBlocksAmount: 0, + lastProposedBlockHeader: null, + proTxInfo: { + type: dashCoreRpcResponse.type, + collateralHash: dashCoreRpcResponse.collateralHash, + collateralIndex: dashCoreRpcResponse.collateralIndex, + collateralAddress: dashCoreRpcResponse.collateralAddress, + operatorReward: dashCoreRpcResponse.operatorReward, + confirmations: dashCoreRpcResponse.confirmations, + state: dashCoreRpcResponse.state + }, + totalReward: 0, + epochReward: 0, + identity: identifier, + identityBalance: 0, + epochInfo: { ...fullEpochInfo }, + withdrawalsCount: 5, + lastWithdrawal: transactions[transactions.length - 1].hash, + lastWithdrawalTime: timestamp.toISOString(), + endpoints + } + + assert.deepEqual(body, expectedValidator) + }) + + it('should return active validator by identifier', async () => { + const [validator] = activeValidators + + const identifier = base58.encode(Buffer.from(validator.pro_tx_hash, 'hex')) + + const { body } = await client.get(`/validator/identity/${identifier}`) + .expect(200) + .expect('Content-Type', 'application/json; charset=utf-8') + + const expectedValidator = { + proTxHash: validator.pro_tx_hash, + isActive: false, + proposedBlocksAmount: blocks.filter((block) => block.validator === validator.pro_tx_hash).length, + lastProposedBlockHeader: blocks + .filter((block) => block.validator === validator.pro_tx_hash) + .map((block) => BlockHeader.fromRow(block)) + .map((blockHeader) => ({ + hash: blockHeader.hash, + height: blockHeader.height, + timestamp: blockHeader.timestamp.toISOString(), + blockVersion: blockHeader.blockVersion, + appVersion: blockHeader.appVersion, + l1LockedHeight: blockHeader.l1LockedHeight, + validator: blockHeader.validator + })) + .toReversed()[0] ?? null, + proTxInfo: { + type: dashCoreRpcResponse.type, + collateralHash: dashCoreRpcResponse.collateralHash, + collateralIndex: dashCoreRpcResponse.collateralIndex, + collateralAddress: dashCoreRpcResponse.collateralAddress, + operatorReward: dashCoreRpcResponse.operatorReward, + confirmations: dashCoreRpcResponse.confirmations, + state: dashCoreRpcResponse.state + }, + totalReward: 0, + epochReward: 0, + identity: identifier, + identityBalance: 0, + epochInfo: { ...fullEpochInfo }, + withdrawalsCount: 5, + lastWithdrawal: transactions[transactions.length - 2].hash, + lastWithdrawalTime: timestamp.toISOString(), + endpoints + } + + assert.deepEqual(body, expectedValidator) + }) + + it('should return 404 if validator not found', async () => { + await client.get('/validator/DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF') + .expect(404) + .expect('Content-Type', 'application/json; charset=utf-8') + }) + }) + describe('getValidators()', async () => { describe('no filter', async () => { it('should return default set of validators', async () => { diff --git a/packages/frontend/src/app/api/content.md b/packages/frontend/src/app/api/content.md index d39e894cd..aaea466f8 100644 --- a/packages/frontend/src/app/api/content.md +++ b/packages/frontend/src/app/api/content.md @@ -15,6 +15,9 @@ Reference: * [Blocks](#blocks) * [Validators](#validators) * [Validator by ProTxHash](#validator-by-protxhash) +* [Validator by Identity](#validator-by-identity) +* [Validator Blocks Statistic](#validator-stats-by-protxhash) +* [Validator Rewards Statistic](#validator-rewards-stats-by-protxhash) * [Transaction by hash](#transaction-by-hash) * [Transactions](#transactions) * [Data Contract By Identifier](#data-contract-by-identifier) @@ -23,6 +26,7 @@ Reference: * [Documents by Data Contract](#documents-by-data-contract) * [Identity by Identifier](#identity-by-identifier) * [Identity by DPNS](#identity-by-dpns) +* [Identity Withdrawals](#identity-withdrawals) * [Identities](#identities) * [Data Contracts by Identity](#data-contracts-by-identity) * [Documents by Identity](#documents-by-identity) @@ -30,6 +34,7 @@ Reference: * [Transfers by Identity](#transfers-by-identity) * [Transactions history](#transactions-history) * [Rate](#rate) +* [Decode Raw Transaction](#decode-raw-transaction) ### Status Returns basic stats and epoch info @@ -265,6 +270,89 @@ Get validator by ProTxHash. ``` GET /validator/F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0 +{ + proTxHash: "F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0", + isActive: true, + proposedBlocksAmount: 5, + lastProposedBlockHeader: { + height: 5, + timestamp: "2024-06-23T13:51:44.154Z", + hash: "7253F441FF6AEAC847F9E03672B9386E35FC8CBCFC4A7CC67557FCA10E342904", + l1LockedHeight: 1337, + appVersion: 1, + blockVersion: 13, + validator: "F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0" + }, + proTxInfo: { + type: "Evo", + collateralHash: "6ce8545e25d4f03aba1527062d9583ae01827c65b234bd979aca5954c6ae3a59", + collateralIndex: 19, + collateralAddress: "yYK3Kiq36Xmf1ButkTUYb1iCNtJfSSM4KH", + operatorReward: 0, + confirmations: 214424, + state: { + version: 2, + service: "35.164.23.245:19999", + registeredHeight: 850334, + lastPaidHeight: 1064721, + consecutivePayments: 0, + PoSePenalty: 0, + PoSeRevivedHeight: 1027671, + PoSeBanHeight: -1, + revocationReason: 0, + ownerAddress: "yWrbg8HNwkogZfqKe1VW8czS9KiqdjvJtE", + votingAddress: "yWrbg8HNwkogZfqKe1VW8czS9KiqdjvJtE", + platformNodeID: "b5f25f8f70cf8d05c2d2970bdf186c994431d84e", + platformP2PPort: 36656, + platformHTTPPort: 1443, + payoutAddress: "yeRZBWYfeNE4yVUHV4ZLs83Ppn9aMRH57A", + pubKeyOperator: "b928fa4e127214ccb2b5de1660b5e371d2f3c9845077bc3900fc6aabe82ddd2e61530be3765cea15752e30fc761ab730", + } + }, + identity: "8tsWRSwsTM5AXv4ViCF9gu39kzjbtfFDM6rCyL2RcFzd", + identityBalance: 0, + epochInfo: { + number: 1982, + firstBlockHeight: 31976, + firstCoreBlockHeight: 1118131, + startTime: 1728488466559, + feeMultiplier: 1, + endTime: 1728492066559 + }, + totalReward: 0, + epochReward: 0, + withdrawalsCount: 1, + lastWithdrawal: "01FE1F00379C66C6E3BFD81A088E57E17613EC36E4FF812458535A8ABCB84047", + lastWithdrawalTime: "2024-10-12T03:15:19.257Z", + endpoints: { + coreP2PPortStatus: { + host: '52.33.28.41', + port: 19999, + status: 'ERROR', + message: null + }, + platformP2PPortStatus: { + host: '52.33.28.41', + port: 36656, + status: 'ERROR', + message: null + }, + platformGrpcPortStatus: { + host: '52.33.28.41', + port: 1443, + status: 'ERROR', + message: null + } + } +} +``` +--- +### Validator by Identity +Get validator by Identity. +* `lastProposedBlockHeader` field is nullable +``` +GET /validator/identity/8tsWRSwsTM5AXv4ViCF9gu39kzjbtfFDM6rCyL2RcFzd + { proTxHash: "F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0", isActive: true, @@ -684,50 +772,35 @@ Return all withdrawals for identity _Note: this request does not contain any pagination data in the response_ * `limit` cannot be more then 100 +* returns 404 `not found` if identity don't have withdrawals +* Pagination always `null` ``` GET /identity/A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb/withdrawals?limit=5 -[ - { - "timestamp": 1729096625509, - "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", - "id": "95eiiqMotMvH23f6cv3BPC4ykcHFWTy2g3baCTWZANAs", - "amount": 200000, - "status": 3 - }, - { - "timestamp": 1729096140465, - "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", - "id": "DJzb8nj7JTHwnvAGEGhyFc5hHLFa5Es9WFAyS4HhhNeF", - "amount": 200000, - "status": 3 - }, - { - "timestamp": 1729096636318, - "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", - "id": "E4gbWCQgqrz9DVrzCeDKhr4PVsfp6CeL5DUAYndRVWdk", - "amount": 200000, - "status": 3 - }, - { - "timestamp": 1729096795042, - "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", - "id": "FouX2qY8Eaxj5rSBrH9uxbhAM16ozrUP4sJwdo9pL7Cr", - "amount": 200000, - "status": 3 +{ + pagination: { + limit: null, + page: null, + total: null }, - { - "timestamp": 1729097247874, - "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", - "id": "9VEpb2aJRnCxfi3LjFXWa1zshkBPfzzHHh5yqEkgqw1t", - "amount": 200000, - "status": 3 - } -] + resultSet: [ + { + "document": "95eiiqMotMvH23f6cv3BPC4ykcHFWTy2g3baCTWZANAs", + "sender": "A1rgGVjRGuznRThdAA316VEEpKuVQ7mV8mBK1BFJvXnb", + "status": "COMPLETE", + "amount": 200000, + "timestamp": 1729096625509, + "withdrawalAddress": "yeRZBWYfeNE4yVUHV4ZLs83Ppn9aMRH57A", + "hash": "113F86F4D1F48159B0D6690F3C5F8F33E39243086C041CF016454A66AD63F025" + }, + ... + ] +} ``` Response codes: ``` 200: OK +404: Not Found 500: Internal Server Error ``` --- @@ -826,10 +899,7 @@ GET /identities/GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec/transactions?page=1 gasUsed: 1337000, status: "SUCCESS", error: null, - owner: { - identifier: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec", - aliases: [] - } + owner: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec" }, ... ] } @@ -919,3 +989,477 @@ Response codes: 500: Internal Server Error 503: Service Temporarily Unavailable ``` +### Decode Raw Transaction +Return a decoded State Transition + +Available transactions type for decode + +| Transition type | type index | +|------------------------------|------------| +| `DATA_CONTRACT_CREATE` | 0 | +| `DOCUMENTS_BATCH` | 1 | +| `IDENTITY_CREATE` | 2 | +| `IDENTITY_TOP_UP` | 3 | +| `DATA_CONTRACT_UPDATE` | 4 | +| `IDENTITY_UPDATE` | 5 | +| `IDENTITY_CREDIT_WITHDRAWAL` | 6 | +| `IDENTITY_CREDIT_TRANSFER` | 7 | +| `MASTERNODE_VOTE` | 8 | + +- `fundingAddress` can be null +- `prefundedBalance` can be null +- `contractBounds` always null + +``` +POST /transaction/decode + +{ + "base64": "AAAA56Y/VzBp5vlrJR8JRCPSDLlaZjngwyM50w8dQAmAe3EAAAAAAAEBAAABYpzp8+tOQ8j6k24W7FXjqo7zZmMZcybMIDLw7VfLT0EAAQZsYWJsZXIWBBIEdHlwZRIGb2JqZWN0Egpwcm9wZXJ0aWVzFgISCmNvbnRyYWN0SWQWBBIEdHlwZRIGc3RyaW5nEgltaW5MZW5ndGgDVhIJbWF4TGVuZ3RoA1gSCHBvc2l0aW9uAwASCXNob3J0TmFtZRYEEgR0eXBlEgZzdHJpbmcSCW1heExlbmd0aANAEgltaW5MZW5ndGgDBhIIcG9zaXRpb24DAhIIcmVxdWlyZWQVAhIJc2hvcnROYW1lEgpjb250cmFjdElkEhRhZGRpdGlvbmFsUHJvcGVydGllcxMACgACQR8AOrSAQ3S/emVWILS8WyHcMA97CtY5rH7dB4DSjAm/0x6DZdZcm8jyGIdIuuTUALR8/N724YhxwhOQHqUm5ipN" +} +``` +#### Responses: +``` +{ + "type": 0, + "internalConfig": { + "canBeDeleted": false, + "readonly": false, + "keepsHistory": false, + "documentsKeepHistoryContractDefault": false, + "documentsMutableContractDefault": true, + "documentsCanBeDeletedContractDefault": true, + "requiresIdentityDecryptionBoundedKey": null, + "requiresIdentityEncryptionBoundedKey": null + }, + "userFeeIncrease": 0, + "identityNonce": 7, + "dataContractId": "4PenkX3rPnwvBNvCwRTXaduxym7XG4yJWQNfiruWwM2N", + "ownerId": "7Yowk46VwwHqmD5yZyyygggh937aP6h2UW7aQWBdWpM5", + "schema": { + "identityVerify": { + "documentsMutable": true, + "canBeDeleted": true, + "type": "object", + "properties": { + "normalizedLabel": { + "position": 0, + "type": "string", + "pattern": "^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-]{0,61}[a-hj-km-np-z0-9]$", + "maxLength": 63, + "description": "Domain label converted to lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'b0b'", + "$comment": "Must match a domain document to provide further information. Must be equal to the label in lowercase. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\"." + }, + "normalizedParentDomainName": { + "type": "string", + "pattern": "^$|^[a-hj-km-np-z0-9][a-hj-km-np-z0-9-\\.]{0,61}[a-hj-km-np-z0-9]$", + "minLength": 0, + "maxLength": 63, + "position": 1, + "description": "A parent domain name in lowercase for case-insensitive uniqueness validation. \"o\", \"i\" and \"l\" replaced with \"0\" and \"1\" to mitigate homograph attack. e.g. 'dash'", + "$comment": "Must either be equal to an existing domain or empty to create a top level domain. \"o\", \"i\" and \"l\" must be replaced with \"0\" and \"1\". Only the data contract owner can create top level domains." + }, + "url": { + "position": 2, + "type": "string", + "description": "The identity verification URL to be stored.", + "maxLength": 128, + "pattern": "^https?://.*", + "format": "uri" + } + }, + "indices": [ + { + "name": "ownerId", + "properties": [ + { + "$ownerId": "asc" + } + ] + }, + { + "name": "ownerId_NormDomainName_NormLabel", + "properties": [ + { + "$ownerId": "asc" + }, + { + "normalizedParentDomainName": "asc" + }, + { + "normalizedLabel": "asc" + } + ] + }, + { + "name": "uniqueUsernameIndex", + "properties": [ + { + "normalizedLabel": "asc" + } + ] + } + ], + "required": [ + "url", + "normalizedLabel", + "normalizedParentDomainName" + ], + "additionalProperties": false + }, + "tx_metadata": { + "type": "object", + "indices": [ + { + "name": "ownerId", + "properties": [ + { + "$ownerId": "asc" + } + ] + }, + { + "name": "ownerIdAndCreatedAt", + "properties": [ + { + "$ownerId": "asc" + }, + { + "$createdAt": "asc" + } + ] + } + ], + "properties": { + "keyIndex": { + "type": "integer", + "minimum": 0, + "description": "The index of the owners identity public key used to derive the encryption key.", + "position": 0 + }, + "encryptionKeyIndex": { + "type": "integer", + "minimum": 0, + "description": "The secondary index used to derive the encryption key that is used to encrypt and decrypt encryptedData.", + "position": 1 + }, + "encryptedMetadata": { + "type": "array", + "byteArray": true, + "minItems": 32, + "maxItems": 4096, + "description": "encrypted metadata using AES-CBC-256", + "position": 2 + } + }, + "required": [ + "keyIndex", + "encryptionKeyIndex", + "encryptedMetadata", + "$createdAt" + ], + "additionalProperties": false + } + }, + "signature": "1ff784ded3a1177d0c6194fadfaf3a3722cb4ef41c1ae3fa7bdeca2f0bd5a19cdc7d95d2286990e4e8a9308043261c4ed7fdb5676d4d5629ef26d26a79a19a0f3a", + "signaturePublicKeyId": 1, + "raw": "00000032609106c20cd642005b3c1ed00dde5cb5fde5f40baaa4184aebbafc560b679f00000000000101000001614c34c98bc3f0a618951f0e61310598a0ad8ec225007f1f001fddf7e7b292f000020e6964656e7469747956657269667916071210646f63756d656e74734d757461626c651301120c63616e426544656c65746564130112047479706512066f626a656374120a70726f706572746965731603120f6e6f726d616c697a65644c6162656c16061208706f736974696f6e02001204747970651206737472696e6712077061747465726e123c5e5b612d686a2d6b6d2d6e702d7a302d395d5b612d686a2d6b6d2d6e702d7a302d392d5d7b302c36317d5b612d686a2d6b6d2d6e702d7a302d395d2412096d61784c656e677468023f120b6465736372697074696f6e12a3446f6d61696e206c6162656c20636f6e76657274656420746f206c6f7765726361736520666f7220636173652d696e73656e73697469766520756e697175656e6573732076616c69646174696f6e2e20226f222c2022692220616e6420226c22207265706c6163656420776974682022302220616e642022312220746f206d6974696761746520686f6d6f67726170682061747461636b2e20652e672e202762306227120824636f6d6d656e7412994d757374206d61746368206120646f6d61696e20646f63756d656e7420746f2070726f76696465206675727468657220696e666f726d6174696f6e2e204d75737420626520657175616c20746f20746865206c6162656c20696e206c6f776572636173652e20226f222c2022692220616e6420226c22206d757374206265207265706c6163656420776974682022302220616e64202231222e121a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6516071204747970651206737472696e6712077061747465726e12415e247c5e5b612d686a2d6b6d2d6e702d7a302d395d5b612d686a2d6b6d2d6e702d7a302d392d5c2e5d7b302c36317d5b612d686a2d6b6d2d6e702d7a302d395d2412096d696e4c656e677468020012096d61784c656e677468023f1208706f736974696f6e0201120b6465736372697074696f6e12a24120706172656e7420646f6d61696e206e616d6520696e206c6f7765726361736520666f7220636173652d696e73656e73697469766520756e697175656e6573732076616c69646174696f6e2e20226f222c2022692220616e6420226c22207265706c6163656420776974682022302220616e642022312220746f206d6974696761746520686f6d6f67726170682061747461636b2e20652e672e20276461736827120824636f6d6d656e7412c04d7573742065697468657220626520657175616c20746f20616e206578697374696e6720646f6d61696e206f7220656d70747920746f20637265617465206120746f70206c6576656c20646f6d61696e2e20226f222c2022692220616e6420226c22206d757374206265207265706c6163656420776974682022302220616e64202231222e204f6e6c7920746865206461746120636f6e7472616374206f776e65722063616e2063726561746520746f70206c6576656c20646f6d61696e732e120375726c16061208706f736974696f6e02021204747970651206737472696e67120b6465736372697074696f6e122b546865206964656e7469747920766572696669636174696f6e2055524c20746f2062652073746f7265642e12096d61784c656e677468028012077061747465726e120c5e68747470733f3a2f2f2e2a1206666f726d617412037572691207696e64696365731503160212046e616d6512076f776e65724964120a70726f70657274696573150116011208246f776e657249641203617363160212046e616d6512206f776e657249645f4e6f726d446f6d61696e4e616d655f4e6f726d4c6162656c120a70726f70657274696573150316011208246f776e6572496412036173631601121a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6512036173631601120f6e6f726d616c697a65644c6162656c1203617363160212046e616d651213756e69717565557365726e616d65496e646578120a70726f7065727469657315011601120f6e6f726d616c697a65644c6162656c1203617363120872657175697265641503120375726c120f6e6f726d616c697a65644c6162656c121a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6512146164646974696f6e616c50726f7065727469657313000b74785f6d65746164617461160512047479706512066f626a6563741207696e64696365731502160212046e616d6512076f776e65724964120a70726f70657274696573150116011208246f776e657249641203617363160212046e616d6512136f776e65724964416e64437265617465644174120a70726f70657274696573150216011208246f776e6572496412036173631601120a246372656174656441741203617363120a70726f70657274696573160312086b6579496e64657816041204747970651207696e746567657212076d696e696d756d0200120b6465736372697074696f6e124e54686520696e646578206f6620746865206f776e657273206964656e74697479207075626c6963206b6579207573656420746f206465726976652074686520656e6372797074696f6e206b65792e1208706f736974696f6e02001212656e6372797074696f6e4b6579496e64657816041204747970651207696e746567657212076d696e696d756d0200120b6465736372697074696f6e1268546865207365636f6e6461727920696e646578207573656420746f206465726976652074686520656e6372797074696f6e206b65792074686174206973207573656420746f20656e637279707420616e64206465637279707420656e63727970746564446174612e1208706f736974696f6e02011211656e637279707465644d657461646174611606120474797065120561727261791209627974654172726179130112086d696e4974656d73022012086d61784974656d7302fb1000120b6465736372697074696f6e1224656e63727970746564206d65746164617461207573696e67204145532d4342432d3235361208706f736974696f6e020212087265717569726564150412086b6579496e6465781212656e6372797074696f6e4b6579496e6465781211656e637279707465644d65746164617461120a2463726561746564417412146164646974696f6e616c50726f706572746965731300070001411ff784ded3a1177d0c6194fadfaf3a3722cb4ef41c1ae3fa7bdeca2f0bd5a19cdc7d95d2286990e4e8a9308043261c4ed7fdb5676d4d5629ef26d26a79a19a0f3a" +} +``` +``` +{ + "type": 1, + "transitions": [ + { + "id": "Fwvkm4ktZVJEHN34RWm4s3fbNXoC3vNj9MB49LwLdavM", + "dataContractId": "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec", + "revision": 1, + "type": "domain", + "action": 0, + "data": { + "label": "Microsoft", + "normalizedLabel": "m1cr0s0ft", + "normalizedParentDomainName": "dash", + "parentDomainName": "dash", + "preorderSalt": "+nE4I9leD/ua1WdxrrZ4gWQWglNUPwRRjgULjvqGOCs=", + "records": { + "identity": "HVfqSPfdmiHsrajx7EmErGnV597uYdH3JGhvwpVDcdAT" + }, + "subdomainRules": { + "allowSubdomains": false + } + }, + "prefundedBalance": { + "parentNameAndLabel": 20000000000 + } + } + ], + "userFeeIncrease": 0, + "signature": "2056f7e8fafc7328ed3026466f90cc0d50ad04714c30d3bc4dc7228440adc2b7974091f27b51dc23de470b5e235741c2a852f74c8ab73d7e83469fe24dcffbe425", + "signaturePublicKeyId": 2, + "ownerId": "HVfqSPfdmiHsrajx7EmErGnV597uYdH3JGhvwpVDcdAT", + "raw": "0200f5132a1cc9642bdab3d06533a91a4f8813129c9a27a73ed53e577e747c9a4eac01000000de15c2d39a27cbe01395094153e58ded53c7cf9fabfe6723810b56e6ee35f50a0706646f6d61696ee668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c53155a1a2c90b886fe21aa4d646b03e0d2775df9a16f06157130247dd435334fc051f07056c6162656c12094d6963726f736f66740f6e6f726d616c697a65644c6162656c12096d31637230733066741a6e6f726d616c697a6564506172656e74446f6d61696e4e616d6512046461736810706172656e74446f6d61696e4e616d651204646173680c7072656f7264657253616c740cfa713823d95e0ffb9ad56771aeb6788164168253543f04518e050b8efa86382b077265636f726473160112086964656e7469747910f5132a1cc9642bdab3d06533a91a4f8813129c9a27a73ed53e577e747c9a4eac0e737562646f6d61696e52756c65731601120f616c6c6f77537562646f6d61696e7313000112706172656e744e616d65416e644c6162656cfd00000004a817c8000002412056f7e8fafc7328ed3026466f90cc0d50ad04714c30d3bc4dc7228440adc2b7974091f27b51dc23de470b5e235741c2a852f74c8ab73d7e83469fe24dcffbe425" +} +``` +``` +IDENTITY_CREATE with chainLock + +{ + "type": 2, + "fundingAddress": null, + "assetLockProof": { + coreChainLockedHeight: 1138871, + type: "chainLock", + fundingAmount: null, + txid: "fc89dd4cbe2518da3cd9737043603e81665df58d4989a38b2942eec56bacad1d", + vout: 0, + fundingAddress: null, + instantLock: null + }, + "userFeeIncrease": 0, + "identityId": "awCc9STLEgw8pJozBq24QzGK5Th9mow7V3EjjY9M7aG", + "signature": "2015bf50b422df6ccfdc4bcdcae76a11106c8f6c627a284f37d2591184e413249350a722926c8899b5514fd94603598031358bc2a0ac031fb402ecc5b8025f2141", + "raw": "0300010000020000000014b23f76cac0218f7637c924e45212bb260cff29250001fc001160b72021007051d2207c45d7592bb7e3e5b4b006a29cfe1899aea8abf00c50ee8a40860000412015bf50b422df6ccfdc4bcdcae76a11106c8f6c627a284f37d2591184e413249350a722926c8899b5514fd94603598031358bc2a0ac031fb402ecc5b8025f214108b1737186062205ee3a5f7e19454121b648e0806c7bc1e8bc073c38217a28e1", + "publicKeys": [ + { + contractBounds: null, + id: 0, + type: "ECDSA_SECP256K1", + data: "0348a6a633850f3c83a0cb30a9fceebbaa3b9ab3f923f123d92728cef234176dc5", + publicKeyHash: "07630dddc55729c043de7bdeb145ee0d44feae3b", + purpose: "AUTHENTICATION", + securityLevel: "MASTER", + readOnly: false, + signature: "2042186a3dec52bfe9a24ee17b98adc5efcbc0a0a6bacbc9627f1405ea5e1bb7ae2bb94a270363400969669e9884ab9967659e9a0d8de7464ee7c47552c8cb0e99" + }, + { + contractBounds: null, + id: 1, + type: "ECDSA_SECP256K1", + data: "034278b0d7f5e6d902ec5a30ae5c656937a0323bdc813e851eb8a2d6a1d23c51cf", + publicKeyHash: "e2615c5ef3f910ebe5ada7930e7b2c04a7ffbb23", + purpose: "AUTHENTICATION", + securityLevel: "HIGH", + readOnly: false, + signature: "1fbb0d0bb63d26c0d5b6e1f4b8c0eebef4d256c4e8aa933a2cb6bd6b2d8aae545215312924c7dd41c963071e2ccfe2187a8684d93c55063cb45fdd03e76344d6a4" + }, + { + contractBounds: null, + id: 2, + type: "ECDSA_SECP256K1", + data: "0245c3b0f0323ddbb9ddf123f939bf37296af4f38fa489aad722c50486575cd8f4", + publicKeyHash: "d53ee3b3518fee80816ab26af98a34ea60ae9af7", + purpose: "AUTHENTICATION", + securityLevel: "CRITICAL", + readOnly: false, + signature: "204013dcca13378b820e40cf1da77abe38662546ef0a304545de3c35845b83a7ad4b42051c2b3539c9181b3f0cb3fb4bc970db89663c6bd6ca1468568a62beaa75" + } + ] +} +``` +``` +IDENTITY_CREATE with instantLock + +{ + "type": 2, + "fundingAddress": "yV1ZYoep5FFSBxKWM24JUwKfnAkFHnXXV7", + "assetLockProof": { + coreChainLockedHeight: null + type: "instantSend", + fundingAmount: 34999000, + txid: "fc89dd4cbe2518da3cd9737043603e81665df58d4989a38b2942eec56bacad1d", + vout: 0, + fundingAddress: "yeMdYXBPum8RmHvrq5SsYE9zNYhMEimbUY", + instantLock: 'AQEKM9t1ICNzvddKryjM4enKn0Y5amBn3o6DwDoC4uk5SAAAAAAdraxrxe5CKYujiUmN9V1mgT5gQ3Bz2TzaGCW+TN2J/JQP49yOk0uJ6el6ls9CmNo++yPYoX1Sx1lWEZTTAAAAhXiuCBXgzawuboxMAXDiXQpJCCPi417VE4mdcYPgTa0/Hd+RCHLAR6H+MXhqKazlGddI7AdWxxLZ94ZvQu+qIpe7G9XRRjQWeYwroIyc6MqQF5mKpvV0AUMYUNMXjCsq' + }, + "userFeeIncrease": 0, + "identityId": "BHAuKDRVPHkJd99pLoQh8dfjUFobwk5bq6enubEBKpsv", + "signature": "1fc5b49ce2feb6cfc94f31de8167b806af0265657d5b8f01584e0db3ca011dba24328998bf40a50dd06b6ab10ed47622f46c07dec4d7cad3625b41aa52c9e11c2f", + "raw": "03000400000000000000210258abe04886308feb52b8f3d64eace4913c9d049f4dda9a88a217e6ca6b89a107411f60451588fe72a067daaa0a1ee04279e77ce346128560129162386f76d51eccdc1d88704f2262fe173de57e5598010655d410da94ae2e1cf7086049878b08e966000100000200002103e6680bb560e40adb299a6b940d3dcbe2b08e6e1a64bc8f6bc9ec3d638655c3554120066559ccd6cea8ac2d366980f77a94cbfdfbd978803edbf4066f42bc53adcdb51956fb0d3c9cec2012583d17b66456094a8620109d6dae29dc562b2870592940000200000100002102326a8c19a1c58d30d142e113d0ddf381d95a6306f56c9ec4d3cb8c4823685b29411f5bb82721b58d92e67db9fb699ab939ccc4a6d5e2e498e80dfb8a3535c13f571923f045e645a898762f8305a4a2218bfedb060f8a8492c48ae9c96247ce17710b00030003010000210252a2d08f295871ec4b04cb0bcf7b2899b0b004702d9058982dd797141d527e78412044820dc7651186634326922eda85741bb3f9f005057d94b36845a7edc16ed1df4d5ccabd7e7f003e9c189847fbc06e943252640bc47963c42ae6c0d87b7b506b00c601014fae5c4ed0e736dd25610b65ff51b56cbe1b9467520f0ced40a9e3b58e4555b10100000077ba9d450b94520434c5d15339922aa7df81b51344b98588649a44752f1a355cd0d05ce3df52f3fb7fc6e64cc414fb7cd9d0ffc4d088e77d9e542fade30000008a8678665212af134cfa36ea40984009adca338efa3131667f5a62b489d2fb2713eb7eccd14dd83cc6679b597548feae18bdc393dae2ab2a50844220359d4b87c428507808dc8df62f56dabb8d1eae2c1859b9ca54b3b4820ebc8453f57c34f6ef03000800014fae5c4ed0e736dd25610b65ff51b56cbe1b9467520f0ced40a9e3b58e4555b1010000006a473044022070293df3b93c523373f1f86272c5dba7886ab41cfc50b0b89658c07d0825c16002201afdf3b31393c5b99373597042b4d651028e824fc12f802aa1be51cc165bcf1e012103d55244573359ad586597b9bb4dd31b8f145121b7c01146656bc26c4b99184a47ffffffff0240420f0000000000026a0049ac4c2e000000001976a91441bb9b42b9f0d589008b4a7f6a72a6bb342b386d88ac0000000024010140420f00000000001976a9145f573cd6a8570cb0b74c4b0ea15334e6bd6b34a788ac0000411fc5b49ce2feb6cfc94f31de8167b806af0265657d5b8f01584e0db3ca011dba24328998bf40a50dd06b6ab10ed47622f46c07dec4d7cad3625b41aa52c9e11c2f98b95bbff1488807c3a4ed36c5fde32f9a6f1e05a622938476652041669e4135", + "publicKeys": [ + { + contractBounds: null, + id: 0, + type: "ECDSA_SECP256K1", + data: "0348a6a633850f3c83a0cb30a9fceebbaa3b9ab3f923f123d92728cef234176dc5", + publicKeyHash: "07630dddc55729c043de7bdeb145ee0d44feae3b", + purpose: "AUTHENTICATION", + securityLevel: "MASTER", + readOnly: false, + signature: "2042186a3dec52bfe9a24ee17b98adc5efcbc0a0a6bacbc9627f1405ea5e1bb7ae2bb94a270363400969669e9884ab9967659e9a0d8de7464ee7c47552c8cb0e99" + }, + { + contractBounds: null, + id: 1, + type: "ECDSA_SECP256K1", + data: "034278b0d7f5e6d902ec5a30ae5c656937a0323bdc813e851eb8a2d6a1d23c51cf", + publicKeyHash: "e2615c5ef3f910ebe5ada7930e7b2c04a7ffbb23", + purpose: "AUTHENTICATION", + securityLevel: "HIGH", + readOnly: false, + signature: "1fbb0d0bb63d26c0d5b6e1f4b8c0eebef4d256c4e8aa933a2cb6bd6b2d8aae545215312924c7dd41c963071e2ccfe2187a8684d93c55063cb45fdd03e76344d6a4" + }, + { + contractBounds: null, + id: 2, + type: "ECDSA_SECP256K1", + data: "0245c3b0f0323ddbb9ddf123f939bf37296af4f38fa489aad722c50486575cd8f4", + publicKeyHash: "d53ee3b3518fee80816ab26af98a34ea60ae9af7", + purpose: "AUTHENTICATION", + securityLevel: "CRITICAL", + readOnly: false, + signature: "204013dcca13378b820e40cf1da77abe38662546ef0a304545de3c35845b83a7ad4b42051c2b3539c9181b3f0cb3fb4bc970db89663c6bd6ca1468568a62beaa75" + } + ] +} +``` +``` +{ + type: 3, + assetLockProof: { + coreChainLockedHeight: null + type: "instantSend", + fundingAmount: 999000, + txid: "7734f498c5b59f64f73070e0a5ec4fa113065da00358223cf888c3c27317ea64", + vout: 0, + fundingAddress: "yWxCwVRgqRmePNPJxezgus1T7xSv5q17SU" + }, + identityId: '4EfA9Jrvv3nnCFdSf7fad59851iiTRZ6Wcu6YVJ4iSeF', + amount: 300000000, + signature: '810cd0bfe02104362941d35bd05fdf82cdc50c3bc8510077bfa62d47b68710', + raw: '040000c60101ecd6b031477f342806df5740b70f93b8a3e925bbf2d90d979a5ed162a8d7d5660000000064ea1773c2c388f83c225803a05d0613a14feca5e07030f7649fb5c598f43477940fe3dc8e934b89e9e97a96cf4298da3efb23d8a17d52c759561194d3000000a5e81597e94558618bf1464801188ecbc09c7a12e73489225c63684259f075f87aa3d47ea9bbbe1f9c314086ddc35a6d18b30ff4fe579855779f9268b8bf5c79760c7d8c56d34163931f016c2e3036852dd33a6b643dd59dc8c54199f34e3d2def0300080001ecd6b031477f342806df5740b70f93b8a3e925bbf2d90d979a5ed162a8d7d566000000006a4730440220339d4d894eb2ff9c193bd8c33cdb3030a8be18ddbf30d983e8286c08c6c4c7d90220181741d9eed3814ec077030c26c0b9fff63b9ef10e1e6ca1c87069b261b0127a0121034951bbd5d0d500942426507d4b84e6d88406300ed82009a8db087f493017786affffffff02e093040000000000026a0078aa0a00000000001976a914706db5d1e8fb5f925c6db64104f4b77f0c8b73d488ac00000000240101e0930400000000001976a91474a509b4f3b80ce818465dc0f9f66e2103d9178b88ac003012c19b98ec0033addb36cd64b7f510670f2a351a4304b5f6994144286efdac411f810cd0bfe02104362941d35bd05fdf82cdc50c3bc8510077bfa62d47b68710' +} +``` +``` +{ + "type": 4, + "internalConfig": { + "canBeDeleted": false, + "readonly": false, + "keepsHistory": false, + "documentsKeepHistoryContractDefault": false, + "documentsMutableContractDefault": true, + "documentsCanBeDeletedContractDefault": true, + "requiresIdentityDecryptionBoundedKey": null, + "requiresIdentityEncryptionBoundedKey": null + }, + "identityContractNonce": 6, + "signaturePublicKeyId": 2, + "signature": "1ff9a776c62ee371a0e5ed95e8efe27c7955f247d5527670e43cbd837e73cfaef3613592b9798e9afd2526e3b92330f07d0c5f1396390d63ad39b4bebeb9c82903", + "userFeeIncrease": 0, + "ownerId": "GgZekwh38XcWQTyWWWvmw6CEYFnLU7yiZFPWZEjqKHit", + "dataContractId": "AJqYb8ZvfbA6ZFgpsvLfpMEzwjaYUPyVmeFxSJrafB18", + "dataContractNonce": 0, + "schema": { + "note": { + "type": "object", + "properties": { + "message": { + "type": "string", + "position": 0 + }, + "author": { + "type": "string", + "position": 1 + } + }, + "additionalProperties": false + } + }, + "version": 2, + "dataContractOwner": "GgZekwh38XcWQTyWWWvmw6CEYFnLU7yiZFPWZEjqKHit", + "raw": "010006008a4af217f340e9c4c95857496cf33b68eb6c712ac6d20a1eb7854d14afd9ffcf00000000000101000002e901dfc172a96ce3f7d334d6c0b69df3b01c86d30ff03a7c24f516838f94340d0001046e6f7465160312047479706512066f626a656374120a70726f70657274696573160212076d65737361676516021204747970651206737472696e671208706f736974696f6e02001206617574686f7216021204747970651206737472696e671208706f736974696f6e020112146164646974696f6e616c50726f7065727469657313000002411ff9a776c62ee371a0e5ed95e8efe27c7955f247d5527670e43cbd837e73cfaef3613592b9798e9afd2526e3b92330f07d0c5f1396390d63ad39b4bebeb9c82903" +} +``` +``` +{ + type: 5, + identityContractNonce: 3, + userFeeIncrease: 0, + identityId: '4NGALjtX2t3AXE3ZCqJiSmYuiWEY3ZPQNUBxNWWRrRSp', + revision: 2, + publicKeysToAdd: [ + { + contractBounds: null, + id: 5, + type: "ECDSA_HASH160", + data: "c208ded6d1af562b8e5387c02a446ea6e8bb325f", + publicKeyHash: "c208ded6d1af562b8e5387c02a446ea6e8bb325f", + purpose: "AUTHENTICATION", + securityLevel: "HIGH", + readOnly: false, + signature: "" + }, + { + contractBounds: null, + id: 6, + type: "ECDSA_SECP256K1", + data: "026213380930c93c4b53f6ddbc5adc5f5165102d8f92f7d9a495a8f9c6e61b30f0", + publicKeyHash: "d39eda042126256a372c388bd191532a7c9612ce", + purpose: "AUTHENTICATION", + securityLevel: "MASTER", + readOnly: false, + signature: "1faf8b0f16320d0f9e29c1db12ab0d3ec87974b19f6fc1189a988cd85503d79f844d3ff778678d7f4f3829891e8e8d0183456194d9fc76ed66e503154996eefe06" + } + ], + setPublicKeyIdsToDisable: [], + signature: '1f341c8eb7b890f416c7a970406dd37da078dab5f2c4aa8dd18375516933b234873127965dd72ee28b7392fcd87e28c4bfef890791b58fa9c34bce9e96d6536cb1', + signaturePublicKeyId: 0, + raw: '0600320566816f366803517a7eb44d331ccb0e442fab6396f3d6ac631b1069aae0410203020005020002000014c208ded6d1af562b8e5387c02a446ea6e8bb325f000006000000000021026213380930c93c4b53f6ddbc5adc5f5165102d8f92f7d9a495a8f9c6e61b30f0411faf8b0f16320d0f9e29c1db12ab0d3ec87974b19f6fc1189a988cd85503d79f844d3ff778678d7f4f3829891e8e8d0183456194d9fc76ed66e503154996eefe06000000411f341c8eb7b890f416c7a970406dd37da078dab5f2c4aa8dd18375516933b234873127965dd72ee28b7392fcd87e28c4bfef890791b58fa9c34bce9e96d6536cb1' +} +``` +``` +{ + "type": 6, + "outputAddress": "yifJkXaxe7oM1NgBDTaXnWa6kXZAazBfjk", + "userFeeIncrease": 0, + "identityContractNonce": 6, + "senderId": "8eTDkBhpQjHeqgbVeriwLeZr1tCa6yBGw76SckvD1cwc", + "amount": 200000, + "identityNonce": 6, + "outputScript": "76a914f51453a538d9a0a9fb3fe0f2948a0f80d9cf525a88ac", + "coreFeePerByte": 5, + "signature": "20cc6d48ed7341d47d6efbdad14ce0f471e67f75110acd56738b7c42c78a71d7da4fd870e1c77934239ea3a0ca0fd1145814b5165bd4ec76e87e774836c680b01b", + "signaturePublicKeyId": 3, + "pooling": "Standard", + "raw": "05017199f1f68404c86ecf60d9cb93aef318fa0f2b08e59ffd176bdef43154ffde6bfc00030d400500011976a914f51453a538d9a0a9fb3fe0f2948a0f80d9cf525a88ac0600034120cc6d48ed7341d47d6efbdad14ce0f471e67f75110acd56738b7c42c78a71d7da4fd870e1c77934239ea3a0ca0fd1145814b5165bd4ec76e87e774836c680b01b" +} +``` +``` +{ + "type": 7, + "identityContractNonce": 1, + "userFeeIncrease": 0, + "senderId": "24YEeZmpy1QNKronDT8enYWLXnfoxYK7hrHUdpWHxURg", + "recipientId": "6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs", + "amount": 21856638, + "signaturePublicKeyId": 3, + "signature": "1f39c5c81434699df7924d68eba4326352ac97883688e3ec3ffed36746d6fb8c227d4a96a40fcd38673f80ed64ab8e3514cf81fe8be319774429071881d3c8b1f8", + "raw": "07000fc3bf4a26bff60f4f79a1f4b929ce4d4c5833d226c1c7f68758e71d7ae229db569fd4f616b3dedecbeef95352cf38f1fb04d232a0d20623bc195b0c3f721840fc014d817e010003411f39c5c81434699df7924d68eba4326352ac97883688e3ec3ffed36746d6fb8c227d4a96a40fcd38673f80ed64ab8e3514cf81fe8be319774429071881d3c8b1f8" +} +``` +``` +{ + "type": 8, + "contestedResourcesVotePoll": [ + "EgRkYXNo", + "EgN5MDE=" + ], + "contractId": "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec", + "modifiedDataIds": [ + "523FUhxg6WEvp24PfjqFAuHFYXW1gkoXdy8QywfriSse" + ], + "ownerId": "523FUhxg6WEvp24PfjqFAuHFYXW1gkoXdy8QywfriSse", + "signature": "2019d90a905092dd3074da3cd42b05abe944d857fc2573e81e1d39a16ba659c00c7b38b88bee46a853c5c30deb9c2ae3abf4fbb781eec12b86a0928ca7b02ced7d", + "documentTypeName": "domain", + "indexName": "parentNameAndLabel", + "choice": "Abstain", + "proTxHash": 'ad4e38fc81da72d61b14238ee6e5b91915554e24d725718800692d3a863c910b', + "raw": "08005b246080ba64350685fe302d3d790f5bb238cb619920d46230c844f079944a233bb2df460e72e3d59e7fe1c082ab3a5bd9445dd0dd5c4894a6d9f0d9ed9404b5000000e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c5315506646f6d61696e12706172656e744e616d65416e644c6162656c021204646173681203793031010c00412019d90a905092dd3074da3cd42b05abe944d857fc2573e81e1d39a16ba659c00c7b38b88bee46a853c5c30deb9c2ae3abf4fbb781eec12b86a0928ca7b02ced7d" +} +``` +Response codes: +``` +200: OK +500: Internal Server Error +503: Service Temporarily Unavailable +``` From 0d604b95e888e448856015e16bbe168acd1b5081 Mon Sep 17 00:00:00 2001 From: owl352 Date: Fri, 29 Nov 2024 22:55:11 +0300 Subject: [PATCH 10/22] lint --- packages/api/src/routes.js | 22 +++++++++---------- .../api/test/integration/validators.spec.js | 8 +++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/api/src/routes.js b/packages/api/src/routes.js index a6b6b5ac5..6dc95cc3a 100644 --- a/packages/api/src/routes.js +++ b/packages/api/src/routes.js @@ -8,17 +8,17 @@ * @param validatorsController {ValidatorsController} */ module.exports = ({ - fastify, - mainController, - epochController, - blocksController, - transactionsController, - dataContractsController, - documentsController, - identitiesController, - validatorsController, - rateController - }) => { + fastify, + mainController, + epochController, + blocksController, + transactionsController, + dataContractsController, + documentsController, + identitiesController, + validatorsController, + rateController +}) => { const routes = [ { path: '/status', diff --git a/packages/api/test/integration/validators.spec.js b/packages/api/test/integration/validators.spec.js index b1588864d..b451712a9 100644 --- a/packages/api/test/integration/validators.spec.js +++ b/packages/api/test/integration/validators.spec.js @@ -158,10 +158,10 @@ describe('Validators routes', () => { type: IDENTITY_CREDIT_WITHDRAWAL, block_hash: blocks[i].hash, owner: base58.encode(Buffer.from(( - (i % 2) - ? inactiveValidators - : activeValidators)[0].pro_tx_hash, - 'hex')) + (i % 2) + ? inactiveValidators + : activeValidators)[0].pro_tx_hash, + 'hex')) } ) From 5c147d4c78209f605adb14219f4ea9f2c078c04a Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 30 Nov 2024 01:09:46 +0300 Subject: [PATCH 11/22] initial commit --- .../api/src/controllers/BlocksController.js | 4 +- packages/api/src/dao/BlocksDAO.js | 45 ++++++++++++++++--- packages/api/src/models/BlockHeader.js | 4 +- packages/api/src/server.js | 2 +- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/packages/api/src/controllers/BlocksController.js b/packages/api/src/controllers/BlocksController.js index 4ad16c727..4f85ab6a6 100644 --- a/packages/api/src/controllers/BlocksController.js +++ b/packages/api/src/controllers/BlocksController.js @@ -1,8 +1,8 @@ const BlocksDAO = require('../dao/BlocksDAO') class BlocksController { - constructor (knex) { - this.blocksDAO = new BlocksDAO(knex) + constructor (knex, dapi) { + this.blocksDAO = new BlocksDAO(knex, dapi) } getBlockByHash = async (request, response) => { diff --git a/packages/api/src/dao/BlocksDAO.js b/packages/api/src/dao/BlocksDAO.js index 05c033a78..e7590f361 100644 --- a/packages/api/src/dao/BlocksDAO.js +++ b/packages/api/src/dao/BlocksDAO.js @@ -1,9 +1,13 @@ const Block = require('../models/Block') const PaginatedResultSet = require('../models/PaginatedResultSet') +const { getAliasInfo } = require('../utils') +const { base58 } = require('@scure/base') +const Transaction = require('../models/Transaction') module.exports = class BlockDAO { - constructor (knex) { + constructor (knex, dapi) { this.knex = knex + this.dapi = dapi } getStats = async () => { @@ -34,19 +38,48 @@ module.exports = class BlockDAO { } getBlockByHash = async (blockHash) => { - const results = await this.knex - .select('blocks.hash as hash', 'state_transitions.hash as st_hash', 'blocks.height as height', 'blocks.timestamp as timestamp', 'blocks.block_version as block_version', 'blocks.app_version as app_version', 'blocks.l1_locked_height as l1_locked_height', 'blocks.validator as validator') - .from('blocks') + const aliasesSubquery = this.knex('identity_aliases') + .select('identity_identifier', this.knex.raw('array_agg(alias) as aliases')) + .groupBy('identity_identifier') + .as('aliases') + + const rows = await this.knex('blocks') + .select( + 'blocks.hash as hash', 'state_transitions.hash as tx_hash', + 'blocks.height as block_height', 'blocks.timestamp as timestamp', + 'blocks.block_version as block_version', 'blocks.app_version as app_version', + 'blocks.l1_locked_height as l1_locked_height', 'blocks.validator as validator', + 'state_transitions.gas_used as gas_used', 'state_transitions.data as data', + 'state_transitions.status as status', 'state_transitions.owner as owner', + 'aliases.aliases as aliases', 'state_transitions.error as error', 'block_hash', + 'state_transitions.index as index', 'state_transitions.type as type' + ) .leftJoin('state_transitions', 'state_transitions.block_hash', 'blocks.hash') + .leftJoin(aliasesSubquery, 'aliases.identity_identifier', 'state_transitions.owner') .whereILike('blocks.hash', blockHash) - const [block] = results + const [block] = rows if (!block) { return null } - const txs = results.reduce((acc, value) => value.st_hash ? [...acc, value.st_hash] : acc, []) + const txs = await Promise.all(rows.map(async (row) => { + const aliases = await Promise.all((row.aliases ?? []).map(async alias => { + const aliasInfo = await getAliasInfo(alias, this.dapi) + + const isLocked = base58.encode( + Buffer.from(aliasInfo.contestedState?.finishedVoteInfo?.wonByIdentityId ?? ''), + 'base64') !== row.identifier + + return { + alias, + status: (aliasInfo.contestedState !== null && isLocked) ? 'locked' : 'ok' + } + })) + + return Transaction.fromRow({ ...row, aliases }) + })) return Block.fromRow({ header: block, txs }) } diff --git a/packages/api/src/models/BlockHeader.js b/packages/api/src/models/BlockHeader.js index 1d53577b5..b6661558d 100644 --- a/packages/api/src/models/BlockHeader.js +++ b/packages/api/src/models/BlockHeader.js @@ -18,7 +18,7 @@ module.exports = class BlockHeader { } // eslint-disable-next-line camelcase - static fromRow ({ hash, height, timestamp, block_version, app_version, l1_locked_height, validator }) { - return new BlockHeader(hash, height, new Date(timestamp), block_version, app_version, l1_locked_height, validator) + static fromRow ({ hash, block_height, timestamp, block_version, app_version, l1_locked_height, validator }) { + return new BlockHeader(hash, block_height, new Date(timestamp), block_version, app_version, l1_locked_height, validator) } } diff --git a/packages/api/src/server.js b/packages/api/src/server.js index dd949976a..e5261ba27 100644 --- a/packages/api/src/server.js +++ b/packages/api/src/server.js @@ -78,7 +78,7 @@ module.exports = { const mainController = new MainController(knex, dapi) const epochController = new EpochController(knex, dapi) - const blocksController = new BlocksController(knex) + const blocksController = new BlocksController(knex, dapi) const transactionsController = new TransactionsController(client, knex, dapi) const dataContractsController = new DataContractsController(knex) const documentsController = new DocumentsController(knex) From 6cad53a615c2a285c8ca7358620fd60de5c80aed Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 30 Nov 2024 02:25:00 +0300 Subject: [PATCH 12/22] tests and names fix --- packages/api/src/dao/BlocksDAO.js | 30 ++++++++++++++------------ packages/api/src/models/BlockHeader.js | 4 ++-- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/api/src/dao/BlocksDAO.js b/packages/api/src/dao/BlocksDAO.js index e7590f361..6bec8622e 100644 --- a/packages/api/src/dao/BlocksDAO.js +++ b/packages/api/src/dao/BlocksDAO.js @@ -46,7 +46,7 @@ module.exports = class BlockDAO { const rows = await this.knex('blocks') .select( 'blocks.hash as hash', 'state_transitions.hash as tx_hash', - 'blocks.height as block_height', 'blocks.timestamp as timestamp', + 'blocks.height as height', 'blocks.timestamp as timestamp', 'blocks.block_version as block_version', 'blocks.app_version as app_version', 'blocks.l1_locked_height as l1_locked_height', 'blocks.validator as validator', 'state_transitions.gas_used as gas_used', 'state_transitions.data as data', @@ -64,22 +64,24 @@ module.exports = class BlockDAO { return null } - const txs = await Promise.all(rows.map(async (row) => { - const aliases = await Promise.all((row.aliases ?? []).map(async alias => { - const aliasInfo = await getAliasInfo(alias, this.dapi) + const txs = block.tx_hash + ? await Promise.all(rows.map(async (row) => { + const aliases = await Promise.all((row.aliases ?? []).map(async alias => { + const aliasInfo = await getAliasInfo(alias, this.dapi) - const isLocked = base58.encode( - Buffer.from(aliasInfo.contestedState?.finishedVoteInfo?.wonByIdentityId ?? ''), - 'base64') !== row.identifier + const isLocked = base58.encode( + Buffer.from(aliasInfo.contestedState?.finishedVoteInfo?.wonByIdentityId ?? ''), + 'base64') !== row.identifier - return { - alias, - status: (aliasInfo.contestedState !== null && isLocked) ? 'locked' : 'ok' - } - })) + return { + alias, + status: (aliasInfo.contestedState !== null && isLocked) ? 'locked' : 'ok' + } + })) - return Transaction.fromRow({ ...row, aliases }) - })) + return Transaction.fromRow({ ...row, aliases }) + })) + : [] return Block.fromRow({ header: block, txs }) } diff --git a/packages/api/src/models/BlockHeader.js b/packages/api/src/models/BlockHeader.js index b6661558d..1d53577b5 100644 --- a/packages/api/src/models/BlockHeader.js +++ b/packages/api/src/models/BlockHeader.js @@ -18,7 +18,7 @@ module.exports = class BlockHeader { } // eslint-disable-next-line camelcase - static fromRow ({ hash, block_height, timestamp, block_version, app_version, l1_locked_height, validator }) { - return new BlockHeader(hash, block_height, new Date(timestamp), block_version, app_version, l1_locked_height, validator) + static fromRow ({ hash, height, timestamp, block_version, app_version, l1_locked_height, validator }) { + return new BlockHeader(hash, height, new Date(timestamp), block_version, app_version, l1_locked_height, validator) } } From 47c526565d65b8d51182c0a2ec6901c381c0b00a Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 30 Nov 2024 03:38:34 +0300 Subject: [PATCH 13/22] README.md update --- packages/api/README.md | 39 ++++++++++++++++------ packages/frontend/src/app/api/content.md | 41 ++++++++++++++++++------ 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/packages/api/README.md b/packages/api/README.md index c1bd695e5..1a9e9014f 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -150,17 +150,38 @@ Get a block by hash GET /block/DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF { + "header": { + "hash": "04D16F8EE2A892E5F9F884C11DB97CD20BAA4A9539111A9131F847B93422DB26", + "height": 37994, + "timestamp": "2024-10-20T21:35:48.669Z", + "blockVersion": 14, + "appVersion": 4, + "l1LockedHeight": 1124953, + "validator": "8917BB546318F3410D1A7901C7B846A73446311B5164B45A03F0E613F208F234" + }, + "txs": [ { - header: { - hash: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", - height: 1337, - timestamp: "2024-03-18T10:13:54.150Z", - blockVersion: 13, - appVersion: 1, - l1LockedHeight: 1337 - }, - txs: ["DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"] + "hash": "49C07BEDB5710565CFC82F678DEB4849D2CA1CCD3DFBA6FDA3F1C0F3C39D0AD9", + "index": 0, + "blockHash": "04D16F8EE2A892E5F9F884C11DB97CD20BAA4A9539111A9131F847B93422DB26", + "blockHeight": 37994, + "type": 1, + "data": "AgDuMmDTP4yp4UxhCAbUbbj9M0NSKtDkSMDXiaFYkDf05gEAAAD8TaL0Ynpk50URo4Lr7GID83h4Q7YxOfxNyBcNWF7mwQEIcHJlb3JkZXLmaMZZr2au4ecsGG3ee1t+Ch1xKgnEDVch9iK/U8UxVe4PfekoUsU6NnJAmQzOoXBkr3P+LpzyoMFt1ppC7LqAARBzYWx0ZWREb21haW5IYXNoDLF9yHanBZpOsaoAIQ7+WgMlafEFgvsSfAqiyosXA967AAABQR8wm64iVoCLY0WmrqLS13iPcikGVcuYsqpuoqIWfYRLLlqXQlyHQ5XnsfTKor5spJtUz8gvlN3//sqH+sI8y/gz", + "timestamp": "2024-10-20T21:35:48.669Z", + "gasUsed": 34509040, + "status": "SUCCESS", + "error": null, + "owner": { + "identifier": "H2pb35GtKpjLinncBYeMsXkdDYXCbsFzzVmssce6pSJ1", + "aliases": [ + { + "alias": "owl352-testnet.dash", + "status": "ok" + } + ] + } } + ] } ``` --- diff --git a/packages/frontend/src/app/api/content.md b/packages/frontend/src/app/api/content.md index b1d02c30b..d10b7de4a 100644 --- a/packages/frontend/src/app/api/content.md +++ b/packages/frontend/src/app/api/content.md @@ -117,17 +117,38 @@ Get a block by hash GET /block/DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF { + "header": { + "hash": "04D16F8EE2A892E5F9F884C11DB97CD20BAA4A9539111A9131F847B93422DB26", + "height": 37994, + "timestamp": "2024-10-20T21:35:48.669Z", + "blockVersion": 14, + "appVersion": 4, + "l1LockedHeight": 1124953, + "validator": "8917BB546318F3410D1A7901C7B846A73446311B5164B45A03F0E613F208F234" + }, + "txs": [ { - header: { - hash: "DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF", - height: 1337, - timestamp: "2024-03-18T10:13:54.150Z", - blockVersion: 13, - appVersion: 1, - l1LockedHeight: 1337 - }, - txs: ["DEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF"] + "hash": "49C07BEDB5710565CFC82F678DEB4849D2CA1CCD3DFBA6FDA3F1C0F3C39D0AD9", + "index": 0, + "blockHash": "04D16F8EE2A892E5F9F884C11DB97CD20BAA4A9539111A9131F847B93422DB26", + "blockHeight": 37994, + "type": 1, + "data": "AgDuMmDTP4yp4UxhCAbUbbj9M0NSKtDkSMDXiaFYkDf05gEAAAD8TaL0Ynpk50URo4Lr7GID83h4Q7YxOfxNyBcNWF7mwQEIcHJlb3JkZXLmaMZZr2au4ecsGG3ee1t+Ch1xKgnEDVch9iK/U8UxVe4PfekoUsU6NnJAmQzOoXBkr3P+LpzyoMFt1ppC7LqAARBzYWx0ZWREb21haW5IYXNoDLF9yHanBZpOsaoAIQ7+WgMlafEFgvsSfAqiyosXA967AAABQR8wm64iVoCLY0WmrqLS13iPcikGVcuYsqpuoqIWfYRLLlqXQlyHQ5XnsfTKor5spJtUz8gvlN3//sqH+sI8y/gz", + "timestamp": "2024-10-20T21:35:48.669Z", + "gasUsed": 34509040, + "status": "SUCCESS", + "error": null, + "owner": { + "identifier": "H2pb35GtKpjLinncBYeMsXkdDYXCbsFzzVmssce6pSJ1", + "aliases": [ + { + "alias": "owl352-testnet.dash", + "status": "ok" + } + ] + } } + ] } ``` --- @@ -1368,8 +1389,8 @@ IDENTITY_CREATE with instantLock "signature": "2019d90a905092dd3074da3cd42b05abe944d857fc2573e81e1d39a16ba659c00c7b38b88bee46a853c5c30deb9c2ae3abf4fbb781eec12b86a0928ca7b02ced7d", "documentTypeName": "domain", "indexName": "parentNameAndLabel", - "proTxHash": 'ad4e38fc81da72d61b14238ee6e5b91915554e24d725718800692d3a863c910b', "choice": "Abstain", + "proTxHash": 'ad4e38fc81da72d61b14238ee6e5b91915554e24d725718800692d3a863c910b', "raw": "08005b246080ba64350685fe302d3d790f5bb238cb619920d46230c844f079944a233bb2df460e72e3d59e7fe1c082ab3a5bd9445dd0dd5c4894a6d9f0d9ed9404b5000000e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c5315506646f6d61696e12706172656e744e616d65416e644c6162656c021204646173681203793031010c00412019d90a905092dd3074da3cd42b05abe944d857fc2573e81e1d39a16ba659c00c7b38b88bee46a853c5c30deb9c2ae3abf4fbb781eec12b86a0928ca7b02ced7d" } ``` From 430ed461b51b40523f4d711129f356fb33692e4f Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 30 Nov 2024 20:43:00 +0300 Subject: [PATCH 14/22] update target branch --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0073215c6..ac5eb26b0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: steps: - name: "Validate branch" - if: ${{github.base_ref != 'develop'}} + if: ${{github.base_ref == 'master'}} run: exit 1 - name: "Validate label" if: | From 34383908a91909050e5a5dcf37c50632d4b51b11 Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 30 Nov 2024 21:07:39 +0300 Subject: [PATCH 15/22] fix --- packages/api/README.md | 4 ++-- .../api/src/controllers/TransactionsController.js | 11 +++-------- packages/api/src/dao/TransactionsDAO.js | 6 +++--- packages/api/src/schemas.js | 6 ++++-- packages/api/test/integration/transactions.spec.js | 8 ++++---- packages/frontend/src/app/api/content.md | 4 ++-- 6 files changed, 18 insertions(+), 21 deletions(-) diff --git a/packages/api/README.md b/packages/api/README.md index 7a1a48e3f..55302d158 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -463,12 +463,12 @@ Status can be either `SUCCESS` or `FAIL`. In case of error tx, message will appe * `limit` cannot be more then 100 * `owner` Identity identifier * `status` can be `SUCCESS`, `FAIL` or `ALL` -* `filters` array of transactions types +* `transactionsTypes` array of transactions types * `min` number of min `gas_used` * `max` number of max `gas_used` ``` -GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL&min=0&max=9999999 +GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&transactionsTypes=0&transactionsTypes=1&status=ALL&min=0&max=9999999 { pagination: { diff --git a/packages/api/src/controllers/TransactionsController.js b/packages/api/src/controllers/TransactionsController.js index 225e9feff..1e15de816 100644 --- a/packages/api/src/controllers/TransactionsController.js +++ b/packages/api/src/controllers/TransactionsController.js @@ -2,7 +2,6 @@ const TransactionsDAO = require('../dao/TransactionsDAO') const utils = require('../utils') const { calculateInterval, iso8601duration } = require('../utils') const Intervals = require('../enums/IntervalsEnum') -const StateTransitionEnum = require('../enums/StateTransitionEnum') class TransactionsController { constructor (client, knex, dapi) { @@ -28,7 +27,7 @@ class TransactionsController { page = 1, limit = 10, order = 'asc', - filters, + transactionsTypes, owner, status = 'ALL', min, @@ -39,11 +38,7 @@ class TransactionsController { return response.status(400).send({ message: `invalid ordering value ${order}. only 'asc' or 'desc' is valid values` }) } - const stateTransitionIndexes = Object.entries(StateTransitionEnum).map(([, entry]) => entry) - - const validatedFilters = filters?.map((filter) => stateTransitionIndexes.includes(filter)) - - if (validatedFilters?.includes(false) || filters?.length === 0) { + if (transactionsTypes?.length === 0 && transactionsTypes) { return response.status(400).send({ message: 'invalid filters values' }) } @@ -51,7 +46,7 @@ class TransactionsController { Number(page ?? 1), Number(limit ?? 10), order, - filters, + transactionsTypes, owner, status, min, diff --git a/packages/api/src/dao/TransactionsDAO.js b/packages/api/src/dao/TransactionsDAO.js index 30b617e74..ec7cbd93d 100644 --- a/packages/api/src/dao/TransactionsDAO.js +++ b/packages/api/src/dao/TransactionsDAO.js @@ -49,17 +49,17 @@ module.exports = class TransactionsDAO { return Transaction.fromRow({ ...row, aliases }) } - getTransactions = async (page, limit, order, filters, owner, status, min, max) => { + getTransactions = async (page, limit, order, transactionsTypes, owner, status, min, max) => { const fromRank = ((page - 1) * limit) + 1 const toRank = fromRank + limit - 1 let filtersQuery = '' const filtersBindings = [] - if (filters) { + if (transactionsTypes) { // Currently knex cannot digest an array of numbers correctly // https://github.com/knex/knex/issues/2060 - filtersQuery = filters.length > 1 ? `type in (${filters.join(',')})` : `type = ${filters[0]}` + filtersQuery = transactionsTypes.length > 1 ? `type in (${transactionsTypes.join(',')})` : `type = ${transactionsTypes[0]}` } if (owner) { diff --git a/packages/api/src/schemas.js b/packages/api/src/schemas.js index 8752c1007..e10cf2b70 100644 --- a/packages/api/src/schemas.js +++ b/packages/api/src/schemas.js @@ -32,10 +32,12 @@ const schemaTypes = [ minimum: 0, maximum: 8 }, - filters: { + transactionsTypes: { type: ['array', 'null'], items: { - type: 'number' + type: 'number', + minimum: 0, + maximum: 8 } }, status: { diff --git a/packages/api/test/integration/transactions.spec.js b/packages/api/test/integration/transactions.spec.js index c73e97201..e11875e18 100644 --- a/packages/api/test/integration/transactions.spec.js +++ b/packages/api/test/integration/transactions.spec.js @@ -277,7 +277,7 @@ describe('Transaction routes', () => { it('should return default set of transactions desc with owner and type filter', async () => { const owner = transactions[0].transaction.owner - const { body } = await client.get(`/transactions?order=desc&owner=${owner}&filters=0&filters=8`) + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transactionsTypes=0&transactionsTypes=8`) .expect(200) .expect('Content-Type', 'application/json; charset=utf-8') @@ -316,7 +316,7 @@ describe('Transaction routes', () => { it('should return default set of transactions desc with owner and type filter and status', async () => { const owner = transactions[0].transaction.owner - const { body } = await client.get(`/transactions?order=desc&owner=${owner}&filters=1&status=FAIL`) + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transactionsTypes=1&status=FAIL`) .expect(200) .expect('Content-Type', 'application/json; charset=utf-8') @@ -353,7 +353,7 @@ describe('Transaction routes', () => { it('should return default set of transactions desc with owner and type filter and min-max', async () => { const owner = transactions[0].transaction.owner - const { body } = await client.get(`/transactions?order=desc&owner=${owner}&filters=0&min=246&max=1107`) + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transactionsTypes=0&min=246&max=1107`) .expect(200) .expect('Content-Type', 'application/json; charset=utf-8') @@ -390,7 +390,7 @@ describe('Transaction routes', () => { it('should return empty set of transactions desc with owner and type filter', async () => { const owner = transactions[0].transaction.owner - const { body } = await client.get(`/transactions?order=desc&owner=${owner}&filters=8`) + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transactionsTypes=7`) .expect(200) .expect('Content-Type', 'application/json; charset=utf-8') diff --git a/packages/frontend/src/app/api/content.md b/packages/frontend/src/app/api/content.md index b0c60b5ce..2f3725c7e 100644 --- a/packages/frontend/src/app/api/content.md +++ b/packages/frontend/src/app/api/content.md @@ -430,12 +430,12 @@ Status can be either `SUCCESS` or `FAIL`. In case of error tx, message will appe * `limit` cannot be more then 100 * `owner` Identity identifier * `status` can be `SUCCESS`, `FAIL` or `ALL` -* `filters` array of transactions types +* `transactionsTypes` array of transactions types * `min` number of min `gas_used` * `max` number of max `gas_used` ``` -GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&filters=0&filters=1&status=ALL&min=0&max=9999999 +GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&transactionsTypes=0&transactionsTypes=1&status=ALL&min=0&max=9999999 { pagination: { From f5998111e338d309e47b33ba75f80455c2bc8bf1 Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 30 Nov 2024 21:27:41 +0300 Subject: [PATCH 16/22] fix --- .../src/controllers/ValidatorsController.js | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/packages/api/src/controllers/ValidatorsController.js b/packages/api/src/controllers/ValidatorsController.js index f5d098e9e..ab30ec7ab 100644 --- a/packages/api/src/controllers/ValidatorsController.js +++ b/packages/api/src/controllers/ValidatorsController.js @@ -86,7 +86,67 @@ class ValidatorsController { const hash = Buffer.from(base58.decode(identifier)).toString('hex') - await this.getValidatorByProTxHash({ ...request, params: { hash } }, response) + const [currentEpoch] = await this.dapi.getEpochsInfo(1) + const epochInfo = Epoch.fromObject(currentEpoch) + + const identityBalance = await this.dapi.getIdentityBalance(identifier) + + const validator = await this.validatorsDAO.getValidatorByProTxHash(hash, identifier, epochInfo) + + if (!validator) { + return response.status(404).send({ message: 'not found' }) + } + + const validators = await TenderdashRPC.getValidators() + + const proTxInfo = await DashCoreRPC.getProTxInfo(validator.proTxHash) + + const isActive = validators.some(validator => validator.pro_tx_hash === hash) + + const [host] = proTxInfo?.state.service ? proTxInfo?.state.service.match(/^\d+\.\d+\.\d+\.\d+/) : [null] + const [servicePort] = proTxInfo?.state.service ? proTxInfo?.state.service.match(/\d+$/) : [null] + + const [coreStatus, platformStatus, grpcStatus] = (await Promise.allSettled([ + checkTcpConnect(servicePort, host), + checkTcpConnect(proTxInfo?.state.platformP2PPort, host), + checkTcpConnect(proTxInfo?.state.platformHTTPPort, host) + ])).map( + (e) => ({ + status: e.value ?? e.reason?.code, + message: e.reason?.message ?? null + })) + + const endpoints = { + coreP2PPortStatus: { + host, + port: Number(servicePort), + ...coreStatus + }, + platformP2PPortStatus: { + host, + port: Number(proTxInfo?.state.platformP2PPort), + ...platformStatus + }, + platformGrpcPortStatus: { + host, + port: Number(proTxInfo?.state.platformHTTPPort ?? 0), + ...grpcStatus + } + } + + response.send( + Validator.fromObject( + { + ...validator, + isActive, + proTxInfo: ProTxInfo.fromObject(proTxInfo), + identifier, + identityBalance, + epochInfo, + endpoints + } + ) + ) } getValidators = async (request, response) => { From a03886bee74b60cbd67716992b741eaca10f6612 Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 30 Nov 2024 21:28:54 +0300 Subject: [PATCH 17/22] revert --- .../src/controllers/ValidatorsController.js | 62 +------------------ 1 file changed, 1 insertion(+), 61 deletions(-) diff --git a/packages/api/src/controllers/ValidatorsController.js b/packages/api/src/controllers/ValidatorsController.js index ab30ec7ab..f5d098e9e 100644 --- a/packages/api/src/controllers/ValidatorsController.js +++ b/packages/api/src/controllers/ValidatorsController.js @@ -86,67 +86,7 @@ class ValidatorsController { const hash = Buffer.from(base58.decode(identifier)).toString('hex') - const [currentEpoch] = await this.dapi.getEpochsInfo(1) - const epochInfo = Epoch.fromObject(currentEpoch) - - const identityBalance = await this.dapi.getIdentityBalance(identifier) - - const validator = await this.validatorsDAO.getValidatorByProTxHash(hash, identifier, epochInfo) - - if (!validator) { - return response.status(404).send({ message: 'not found' }) - } - - const validators = await TenderdashRPC.getValidators() - - const proTxInfo = await DashCoreRPC.getProTxInfo(validator.proTxHash) - - const isActive = validators.some(validator => validator.pro_tx_hash === hash) - - const [host] = proTxInfo?.state.service ? proTxInfo?.state.service.match(/^\d+\.\d+\.\d+\.\d+/) : [null] - const [servicePort] = proTxInfo?.state.service ? proTxInfo?.state.service.match(/\d+$/) : [null] - - const [coreStatus, platformStatus, grpcStatus] = (await Promise.allSettled([ - checkTcpConnect(servicePort, host), - checkTcpConnect(proTxInfo?.state.platformP2PPort, host), - checkTcpConnect(proTxInfo?.state.platformHTTPPort, host) - ])).map( - (e) => ({ - status: e.value ?? e.reason?.code, - message: e.reason?.message ?? null - })) - - const endpoints = { - coreP2PPortStatus: { - host, - port: Number(servicePort), - ...coreStatus - }, - platformP2PPortStatus: { - host, - port: Number(proTxInfo?.state.platformP2PPort), - ...platformStatus - }, - platformGrpcPortStatus: { - host, - port: Number(proTxInfo?.state.platformHTTPPort ?? 0), - ...grpcStatus - } - } - - response.send( - Validator.fromObject( - { - ...validator, - isActive, - proTxInfo: ProTxInfo.fromObject(proTxInfo), - identifier, - identityBalance, - epochInfo, - endpoints - } - ) - ) + await this.getValidatorByProTxHash({ ...request, params: { hash } }, response) } getValidators = async (request, response) => { From 5a0037bb2796dcc7d539f7baf130c6127fb9900d Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 30 Nov 2024 21:39:54 +0300 Subject: [PATCH 18/22] fix --- packages/api/README.md | 6 +++--- .../api/src/controllers/ValidatorsController.js | 6 +++--- packages/api/src/routes.js | 2 +- packages/frontend/src/app/api/content.md | 15 ++++++--------- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/packages/api/README.md b/packages/api/README.md index 7ba885d9b..365d50f0f 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -48,7 +48,7 @@ Reference: * [Blocks](#blocks) * [Validators](#validators) * [Validator by ProTxHash](#validator-by-protxhash) -* [Validator by Identity](#validator-by-identity) +* [Validator by Masternode Identifier](#validator-by-masternode-identifier) * [Validator Blocks Statistic](#validator-stats-by-protxhash) * [Validator Rewards Statistic](#validator-rewards-stats-by-protxhash) * [Transaction by hash](#transaction-by-hash) @@ -380,8 +380,8 @@ GET /validator/F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0 } ``` --- -### Validator by Identity -Get validator by Identity. +### Validator by Masternode Identifier +Get validator by Masternode Identity. * `lastProposedBlockHeader` field is nullable ``` GET /validator/identity/8tsWRSwsTM5AXv4ViCF9gu39kzjbtfFDM6rCyL2RcFzd diff --git a/packages/api/src/controllers/ValidatorsController.js b/packages/api/src/controllers/ValidatorsController.js index f5d098e9e..ea7de59f0 100644 --- a/packages/api/src/controllers/ValidatorsController.js +++ b/packages/api/src/controllers/ValidatorsController.js @@ -81,12 +81,12 @@ class ValidatorsController { ) } - getValidatorByIdentifier = async (request, response) => { + getValidatorByMasternodeIdentifier = async (request, response) => { const { identifier } = request.params - const hash = Buffer.from(base58.decode(identifier)).toString('hex') + const proTxHash = Buffer.from(base58.decode(identifier)).toString('hex') - await this.getValidatorByProTxHash({ ...request, params: { hash } }, response) + await this.getValidatorByProTxHash({ ...request, params: { hash: proTxHash } }, response) } getValidators = async (request, response) => { diff --git a/packages/api/src/routes.js b/packages/api/src/routes.js index 6dc95cc3a..7bdb4c2f7 100644 --- a/packages/api/src/routes.js +++ b/packages/api/src/routes.js @@ -297,7 +297,7 @@ module.exports = ({ { path: '/validator/identity/:identifier', method: 'GET', - handler: validatorsController.getValidatorByIdentifier, + handler: validatorsController.getValidatorByMasternodeIdentifier, schema: { params: { type: 'object', diff --git a/packages/frontend/src/app/api/content.md b/packages/frontend/src/app/api/content.md index 39fce011f..95336ffbf 100644 --- a/packages/frontend/src/app/api/content.md +++ b/packages/frontend/src/app/api/content.md @@ -15,7 +15,7 @@ Reference: * [Blocks](#blocks) * [Validators](#validators) * [Validator by ProTxHash](#validator-by-protxhash) -* [Validator by Identity](#validator-by-identity) +* [Validator by Masternode Identifier](#validator-by-masternode-identifier) * [Validator Blocks Statistic](#validator-stats-by-protxhash) * [Validator Rewards Statistic](#validator-rewards-stats-by-protxhash) * [Transaction by hash](#transaction-by-hash) @@ -347,8 +347,8 @@ GET /validator/F60A6BF9EC0794BB0CFD1E0F2217933F4B33EDE6FE810692BC275CA18148AEF0 } ``` --- -### Validator by Identity -Get validator by Identity. +### Validator by Masternode Identifier +Get validator by Masternode Identity. * `lastProposedBlockHeader` field is nullable ``` GET /validator/identity/8tsWRSwsTM5AXv4ViCF9gu39kzjbtfFDM6rCyL2RcFzd @@ -879,7 +879,7 @@ Status can be either `SUCCESS` or `FAIL`. In case of error tx, message will appe * `limit` cannot be more then 100 ``` -GET /identity/GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec/transactions?page=1&limit=10&order=asc +GET /identities/GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec/transactions?page=1&limit=10&order=asc { pagination: { @@ -899,10 +899,7 @@ GET /identity/GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec/transactions?page=1&l gasUsed: 1337000, status: "SUCCESS", error: null, - owner: { - identifier: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec", - aliases: [] - } + owner: "GWRSAVFMjXx8HpQFaNJMqBV7MBgMK4br5UESsB4S31Ec" }, ... ] } @@ -1455,8 +1452,8 @@ IDENTITY_CREATE with instantLock "signature": "2019d90a905092dd3074da3cd42b05abe944d857fc2573e81e1d39a16ba659c00c7b38b88bee46a853c5c30deb9c2ae3abf4fbb781eec12b86a0928ca7b02ced7d", "documentTypeName": "domain", "indexName": "parentNameAndLabel", - "proTxHash": 'ad4e38fc81da72d61b14238ee6e5b91915554e24d725718800692d3a863c910b', "choice": "Abstain", + "proTxHash": 'ad4e38fc81da72d61b14238ee6e5b91915554e24d725718800692d3a863c910b', "raw": "08005b246080ba64350685fe302d3d790f5bb238cb619920d46230c844f079944a233bb2df460e72e3d59e7fe1c082ab3a5bd9445dd0dd5c4894a6d9f0d9ed9404b5000000e668c659af66aee1e72c186dde7b5b7e0a1d712a09c40d5721f622bf53c5315506646f6d61696e12706172656e744e616d65416e644c6162656c021204646173681203793031010c00412019d90a905092dd3074da3cd42b05abe944d857fc2573e81e1d39a16ba659c00c7b38b88bee46a853c5c30deb9c2ae3abf4fbb781eec12b86a0928ca7b02ced7d" } ``` From e74a80ebac48678096c40699cc14abdadf2a86d2 Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 30 Nov 2024 21:51:49 +0300 Subject: [PATCH 19/22] fix --- packages/api/README.md | 8 ++++---- .../src/controllers/TransactionsController.js | 20 +++++++++---------- packages/api/src/schemas.js | 6 +++--- .../api/test/integration/transactions.spec.js | 8 ++++---- packages/frontend/src/app/api/content.md | 8 ++++---- 5 files changed, 24 insertions(+), 26 deletions(-) diff --git a/packages/api/README.md b/packages/api/README.md index 55302d158..b29b213d6 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -463,12 +463,12 @@ Status can be either `SUCCESS` or `FAIL`. In case of error tx, message will appe * `limit` cannot be more then 100 * `owner` Identity identifier * `status` can be `SUCCESS`, `FAIL` or `ALL` -* `transactionsTypes` array of transactions types -* `min` number of min `gas_used` -* `max` number of max `gas_used` +* `transaction_type` number of tx type. Can be set multiple times +* `gas_min` number of min `gas_used` +* `gas_max` number of max `gas_used` ``` -GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&transactionsTypes=0&transactionsTypes=1&status=ALL&min=0&max=9999999 +GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&transaction_type=0&transaction_type=1&status=ALL&gas_min=0&gas_max=9999999 { pagination: { diff --git a/packages/api/src/controllers/TransactionsController.js b/packages/api/src/controllers/TransactionsController.js index 1e15de816..1fb088a25 100644 --- a/packages/api/src/controllers/TransactionsController.js +++ b/packages/api/src/controllers/TransactionsController.js @@ -24,21 +24,19 @@ class TransactionsController { getTransactions = async (request, response) => { const { - page = 1, - limit = 10, - order = 'asc', - transactionsTypes, - owner, + page = 1, limit = 10, + order = 'asc', owner, status = 'ALL', - min, - max + // eslint-disable-next-line camelcase + gas_min, gas_max, transaction_type } = request.query if (order !== 'asc' && order !== 'desc') { return response.status(400).send({ message: `invalid ordering value ${order}. only 'asc' or 'desc' is valid values` }) } - if (transactionsTypes?.length === 0 && transactionsTypes) { + // eslint-disable-next-line camelcase + if (transaction_type?.length === 0 && transaction_type) { return response.status(400).send({ message: 'invalid filters values' }) } @@ -46,11 +44,11 @@ class TransactionsController { Number(page ?? 1), Number(limit ?? 10), order, - transactionsTypes, + transaction_type, owner, status, - min, - max + gas_min, + gas_max ) response.send(transactions) diff --git a/packages/api/src/schemas.js b/packages/api/src/schemas.js index e10cf2b70..675271b75 100644 --- a/packages/api/src/schemas.js +++ b/packages/api/src/schemas.js @@ -32,7 +32,7 @@ const schemaTypes = [ minimum: 0, maximum: 8 }, - transactionsTypes: { + transaction_type: { type: ['array', 'null'], items: { type: 'number', @@ -47,10 +47,10 @@ const schemaTypes = [ owner: { type: ['string', 'null'] }, - min: { + gas_min: { type: ['number', 'null'] }, - max: { + gas_max: { type: ['number', 'null'] } } diff --git a/packages/api/test/integration/transactions.spec.js b/packages/api/test/integration/transactions.spec.js index e11875e18..552a58485 100644 --- a/packages/api/test/integration/transactions.spec.js +++ b/packages/api/test/integration/transactions.spec.js @@ -277,7 +277,7 @@ describe('Transaction routes', () => { it('should return default set of transactions desc with owner and type filter', async () => { const owner = transactions[0].transaction.owner - const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transactionsTypes=0&transactionsTypes=8`) + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transaction_type=0&transaction_type=8`) .expect(200) .expect('Content-Type', 'application/json; charset=utf-8') @@ -316,7 +316,7 @@ describe('Transaction routes', () => { it('should return default set of transactions desc with owner and type filter and status', async () => { const owner = transactions[0].transaction.owner - const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transactionsTypes=1&status=FAIL`) + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transaction_type=1&status=FAIL`) .expect(200) .expect('Content-Type', 'application/json; charset=utf-8') @@ -353,7 +353,7 @@ describe('Transaction routes', () => { it('should return default set of transactions desc with owner and type filter and min-max', async () => { const owner = transactions[0].transaction.owner - const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transactionsTypes=0&min=246&max=1107`) + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transaction_type=0&gas_min=246&gas_max=1107`) .expect(200) .expect('Content-Type', 'application/json; charset=utf-8') @@ -390,7 +390,7 @@ describe('Transaction routes', () => { it('should return empty set of transactions desc with owner and type filter', async () => { const owner = transactions[0].transaction.owner - const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transactionsTypes=7`) + const { body } = await client.get(`/transactions?order=desc&owner=${owner}&transaction_type=7`) .expect(200) .expect('Content-Type', 'application/json; charset=utf-8') diff --git a/packages/frontend/src/app/api/content.md b/packages/frontend/src/app/api/content.md index 2f3725c7e..92eb624e7 100644 --- a/packages/frontend/src/app/api/content.md +++ b/packages/frontend/src/app/api/content.md @@ -430,12 +430,12 @@ Status can be either `SUCCESS` or `FAIL`. In case of error tx, message will appe * `limit` cannot be more then 100 * `owner` Identity identifier * `status` can be `SUCCESS`, `FAIL` or `ALL` -* `transactionsTypes` array of transactions types -* `min` number of min `gas_used` -* `max` number of max `gas_used` +* `transaction_type` number of tx type. Can be set multiple times +* `gas_min` number of min `gas_used` +* `gas_max` number of max `gas_used` ``` -GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&transactionsTypes=0&transactionsTypes=1&status=ALL&min=0&max=9999999 +GET /transactions?=1&limit=10&order=asc&owner=6q9RFbeea73tE31LGMBLFZhtBUX3wZL3TcNynqE18Zgs&transaction_type=0&transaction_type=1&status=ALL&gas_min=0&gas_max=9999999 { pagination: { From 27eaedfb13007dcee85f3391be30f7f1d6ed7571 Mon Sep 17 00:00:00 2001 From: owl352 Date: Sat, 30 Nov 2024 22:13:59 +0300 Subject: [PATCH 20/22] tests fix --- .../api/src/controllers/MainController.js | 2 +- packages/api/test/integration/main.spec.js | 62 ++++++++++++++++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/packages/api/src/controllers/MainController.js b/packages/api/src/controllers/MainController.js index b42535c23..61ce772a6 100644 --- a/packages/api/src/controllers/MainController.js +++ b/packages/api/src/controllers/MainController.js @@ -12,7 +12,7 @@ const PLATFORM_VERSION = '1' + require('../../package.json').dependencies.dash.s class MainController { constructor (knex, dapi) { - this.blocksDAO = new BlocksDAO(knex) + this.blocksDAO = new BlocksDAO(knex, dapi) this.dataContractsDAO = new DataContractsDAO(knex) this.documentsDAO = new DocumentsDAO(knex) this.transactionsDAO = new TransactionsDAO(knex, dapi) diff --git a/packages/api/test/integration/main.spec.js b/packages/api/test/integration/main.spec.js index e8fdee4fd..45adfc864 100644 --- a/packages/api/test/integration/main.spec.js +++ b/packages/api/test/integration/main.spec.js @@ -149,14 +149,72 @@ describe('Other routes', () => { const expectedBlock = { header: { hash: block.hash, - height: block.height, + height: 1, timestamp: block.timestamp.toISOString(), blockVersion: block.block_version, appVersion: block.app_version, l1LockedHeight: block.l1_locked_height, validator: block.validator }, - txs: [identityTransaction.hash, dataContractTransaction.hash, documentTransaction.hash] + txs: [ + { + hash: identityTransaction.hash, + index: identityTransaction.index, + blockHash: identityTransaction.block_hash, + blockHeight: null, + type: identityTransaction.type, + data: '{}', + timestamp: block.timestamp.toISOString(), + gasUsed: 0, + status: 'SUCCESS', + error: null, + owner: { + identifier: identityTransaction.owner, + aliases: [{ + alias: 'dpns.dash', + status: 'ok' + }] + } + }, + { + hash: dataContractTransaction.hash, + index: dataContractTransaction.index, + blockHash: dataContractTransaction.block_hash, + blockHeight: null, + type: dataContractTransaction.type, + data: '{}', + timestamp: block.timestamp.toISOString(), + gasUsed: 0, + status: 'SUCCESS', + error: null, + owner: { + identifier: dataContractTransaction.owner, + aliases: [{ + alias: 'dpns.dash', + status: 'ok' + }] + } + }, + { + hash: documentTransaction.hash, + index: documentTransaction.index, + blockHash: documentTransaction.block_hash, + blockHeight: null, + type: documentTransaction.type, + data: '{}', + timestamp: block.timestamp.toISOString(), + gasUsed: 0, + status: 'SUCCESS', + error: null, + owner: { + identifier: documentTransaction.owner, + aliases: [{ + alias: 'dpns.dash', + status: 'ok' + }] + } + } + ] } assert.deepEqual({ block: expectedBlock }, body) From 11d91782771d6c3aa47732f8a5ab8a868cfd3508 Mon Sep 17 00:00:00 2001 From: owl352 Date: Sun, 1 Dec 2024 23:58:28 +0300 Subject: [PATCH 21/22] tests fix --- 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 45adfc864..209805df2 100644 --- a/packages/api/test/integration/main.spec.js +++ b/packages/api/test/integration/main.spec.js @@ -85,7 +85,8 @@ describe('Other routes', () => { dataContractTransaction = await fixtures.transaction(knex, { block_hash: block.hash, type: StateTransitionEnum.DATA_CONTRACT_CREATE, - owner: identity.identifier + owner: identity.identifier, + index: 1 }) dataContract = await fixtures.dataContract(knex, { state_transition_hash: dataContractTransaction.hash, @@ -95,7 +96,8 @@ describe('Other routes', () => { documentTransaction = await fixtures.transaction(knex, { block_hash: block.hash, type: StateTransitionEnum.DOCUMENTS_BATCH, - owner: identity.identifier + owner: identity.identifier, + index: 2 }) await fixtures.document(knex, { state_transition_hash: documentTransaction.hash, From d6f53548a3366c4e1ba3afa400268ac173eb1ead Mon Sep 17 00:00:00 2001 From: owl352 Date: Mon, 2 Dec 2024 00:02:55 +0300 Subject: [PATCH 22/22] tests fix --- packages/api/src/dao/BlocksDAO.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/api/src/dao/BlocksDAO.js b/packages/api/src/dao/BlocksDAO.js index 6bec8622e..94d56c912 100644 --- a/packages/api/src/dao/BlocksDAO.js +++ b/packages/api/src/dao/BlocksDAO.js @@ -57,6 +57,7 @@ module.exports = class BlockDAO { .leftJoin('state_transitions', 'state_transitions.block_hash', 'blocks.hash') .leftJoin(aliasesSubquery, 'aliases.identity_identifier', 'state_transitions.owner') .whereILike('blocks.hash', blockHash) + .orderBy('state_transitions.index', 'asc') const [block] = rows