diff --git a/.github/workflows/deploy-libre-testnet.yaml b/.github/workflows/deploy-libre-testnet.yaml index c3fb83cd..ff3223c6 100644 --- a/.github/workflows/deploy-libre-testnet.yaml +++ b/.github/workflows/deploy-libre-testnet.yaml @@ -37,7 +37,7 @@ jobs: REACT_APP_TITLE: 'Libre Testnet Network Dashboard' REACT_APP_VERSION: ${{github.ref}} REACT_APP_DEFAULT_PRODUCER_LOGO: 'https://antelope.tools/images/libre.png' - REACT_APP_FOOTER_LINKS: '[{ "text": "Libre Website", "src": "https://libre.org/" },{ "text": "Libre Block Explorer", "src": "https://libre-testnet-explorer.edenia.cloud/" },{"text": "Documentation","src": "https://libre-chain.gitbook.io/"},{"text": "Libre Network Monitor","src": "https://libre-testnet.antelope.tools"}]' + REACT_APP_FOOTER_LINKS: '[{ "text": "Libre Website", "src": "https://libre.org/" },{ "text": "Libre Block Explorer", "src": "https://testnet.libre.org/v2/explore" },{"text": "Documentation","src": "https://libre-chain.gitbook.io/"},{"text": "Libre Network Monitor","src": "https://libre-testnet.antelope.tools"}]' REACT_APP_EOS_RATE_LINK: '' REACT_APP_USE_REWARDS: 'true' REACT_APP_USE_VOTES: 'true' @@ -57,7 +57,7 @@ jobs: REACT_APP_TOKEN_SYMBOL: 'LIBRE' REACT_APP_NETWORK_URL: '[{"label":"EOS","value":"https://eos.antelope.tools","mainnet":true,"pair":"eos","icon":"eos","order":1},{"label":"Proton","value":"https://proton.antelope.tools","mainnet":true,"pair":"proton","icon":"proton","order":2},{"label":"WAX","value":"https://wax.antelope.tools","mainnet":true,"pair":"wax","icon":"wax","order":3},{"label":"Telos","value":"https://telos.antelope.tools","mainnet":true,"pair":"telos","icon":"telos","order":4},{"label":"Libre","value":"https://libre.antelope.tools","mainnet":true,"pair":"libre","icon":"libre","order":5},{"label":"LACChain EOSIO","value":"https://lacchain.antelope.tools","mainnet":true,"pair":null,"icon":"lacchain","order":6},{"label":"Jungle4 Testnet","value":"https://jungle.antelope.tools","mainnet":false,"pair":"eos","icon":"jungle","order":1},{"label":"Proton Testnet","value":"https://proton-testnet.antelope.tools","mainnet":false,"pair":"proton","icon":"proton","order":2},{"label":"WAX Testnet","value":"https://wax-testnet.antelope.tools","mainnet":false,"pair":"wax","icon":"wax","order":3},{"label":"Telos Testnet","value":"https://telos-testnet.antelope.tools","mainnet":false,"pair":"telos","icon":"telos","order":4},{"label":"Libre Testnet","value":"https://libre-testnet.antelope.tools","mainnet":false,"pair":"libre","icon":"libre","order":5},{"label":"Ultra Testnet","value":"https://ultra-testnet.antelope.tools","mainnet":false,"pair":"ultra","icon":"ultra","order":6}]' REACT_APP_DISABLED_MENU_ITEMS: '["/missed-blocks"]' - REACT_APP_BLOCK_EXPLORER_URL: 'https://libre-testnet-explorer.edenia.cloud/transaction/(transaction)' + REACT_APP_BLOCK_EXPLORER_URL: 'https://testnet.libre.org/v2/explore/transaction/(transaction)' REACT_APP_STATE_HISTORY_ENABLED: 'true' REACT_APP_GOOGLE_ANALITIC_PAGE_ID: 'G-E6Y0EC9FT8' REACT_APP_PUBLIC_RE_CAPTCHA_KEY: ${{ secrets.REACT_APP_PUBLIC_RE_CAPTCHA_KEY }} @@ -85,7 +85,7 @@ jobs: HAPI_EOS_STATE_HISTORY_PLUGIN_ENDPOINT: 'ws://api-node.libre-testnet:8080' HAPI_EOS_MISSED_BLOCKS_ENABLED: 'false' HAPI_EOS_BLOCK_HISTORY_DAYS: 90 - HAPI_EOS_MAX_CPU_BLOCK: 100000 + HAPI_EOS_MAX_CPU_BLOCK: 250000 HAPI_EOS_MAX_NET_BLOCK: 1048576 HAPI_EOS_API_CHAIN_ID: b64646740308df2ee06c6b72f34c0f7fa066d940e831f752db2006fcc2b78dee HAPI_EOS_BASE_ACCOUNT: ${{ secrets.HAPI_EOS_BASE_ACCOUNT }} diff --git a/hapi/src/services/stats.service.js b/hapi/src/services/stats.service.js index 2319a6fe..47e95f33 100644 --- a/hapi/src/services/stats.service.js +++ b/hapi/src/services/stats.service.js @@ -2,7 +2,7 @@ const Boom = require('@hapi/boom') const { StatusCodes } = require('http-status-codes') const moment = require('moment') -const { hasuraUtil, sequelizeUtil, sleepFor, eosUtil } = require('../utils') +const { hasuraUtil, sequelizeUtil, sleepFor } = require('../utils') const STAT_ID = 'bceb5b75-6cb9-45af-9735-5389e0664847' @@ -44,14 +44,16 @@ const _getMissedBlock = async (start, end) => { const getTransactionsInTimeRage = async (start, end) => { const [rows] = await sequelizeUtil.query(` SELECT - sum(transactions_length)::integer as transactions_count + sum(transactions_length)::integer as transactions_count, + avg(block_history.cpu_usage)::numeric(5,2) as cpu_usage, + avg(block_history.net_usage)::numeric(6,3) as net_usage FROM block_history WHERE timestamp between '${start.toISOString()}' and '${end.toISOString()}' `) - return rows?.[0]?.transactions_count || 0 + return rows?.[0] } const getNodesSummary = async () => { @@ -67,7 +69,7 @@ const getNodesSummary = async () => { type `) - rows.forEach(row => { + rows.forEach((row) => { payload[row.node_type || 'unknown'] = row.nodes_count total += row.nodes_count }) @@ -119,12 +121,12 @@ const getBlockDistribution = async (range = '1 day') => { ` const data = await hasuraUtil.request(query, { startTime }) - data.items.forEach(item => { + data.items.forEach((item) => { totalBloks += item.blocks }) return data.items - .map(item => ({ + .map((item) => ({ account: item.producer || 'N/A', blocks: item.blocks, percent: item.blocks === 0 ? 0 : item.blocks / totalBloks @@ -225,7 +227,7 @@ const getCurrentMissedBlock = async () => { let newData = data - rows.forEach(element => { + rows.forEach((element) => { if (newData[element.account]) { newData = { ...newData, @@ -248,7 +250,7 @@ const getCurrentMissedBlock = async () => { getCurrentMissedBlock() } -const udpateStats = async payload => { +const udpateStats = async (payload) => { const mutation = ` mutation ($id: uuid!, $payload: stat_set_input!) { update_stat_by_pk(pk_columns: {id: $id}, _set: $payload) { @@ -259,7 +261,7 @@ const udpateStats = async payload => { await hasuraUtil.request(mutation, { id: STAT_ID, payload }) } -const insertStats = async payload => { +const insertStats = async (payload) => { const mutation = ` mutation ($payload: stat_insert_input!) { insert_stat_one(object: $payload) { @@ -275,65 +277,49 @@ const getLastTPSAllTimeHigh = async () => { if (!stats || !stats.last_block_at) return null - if (stats.tps_all_time_high) { - return { - ...stats.tps_all_time_high, - last_block_at: stats.last_block_at - } - } - - const scheduleHistoryInfo = await _getScheduleHystory() - - if (!scheduleHistoryInfo) return null - - return { - transactions_count: 0, - last_block_at: stats.last_block_at, - checked_at: moment(scheduleHistoryInfo.first_block_at) - .subtract(1, 'second') - .toISOString() - } + return stats.tps_all_time_high } -const getBlockUsage = async blockNum => { - const block = await eosUtil.getBlock(blockNum) - const info = await eosUtil.getInfo() - let cpuUsage = 0 - let netUsage = 0 +const getTimestampBlock = async (position) => { + const query = ` + query { + blocks: block_history(limit: 1, order_by: {block_num: ${ + position === 'first' ? 'asc' : 'desc' + }}, where: {producer: {_neq: "NULL"}}) { + timestamp + } + } + ` - block.transactions.forEach(transaction => { - cpuUsage += transaction.cpu_usage_us - netUsage += transaction.net_usage_words - }) + const data = await hasuraUtil.request(query) - return { - id: block.id, - block_num: block.block_num, - transactions_count: block.transactions.length, - cpu_usage_us: cpuUsage, - block_cpu_limit: info.block_cpu_limit, - cpu_usage_percent: cpuUsage / info.block_cpu_limit, - net_usage_words: netUsage, - block_net_limit: info.block_net_limit, - net_usage_percent: netUsage / info.block_net_limit - } + return data?.blocks[0]?.timestamp } const syncTPSAllTimeHigh = async () => { const lastValue = await getLastTPSAllTimeHigh() + let start + let end + if (!lastValue) { - await sleepFor(60) - syncTPSAllTimeHigh() + const firstBlockInDB = new Date(await getTimestampBlock('first')) - return + start = moment(firstBlockInDB) + end = moment(start).add( + 59 + (500 - firstBlockInDB.getMilliseconds()) / 1000, + 'seconds' + ) + } else { + start = moment(lastValue.checked_at).add(0.5, 'second') + end = moment(start).add(59.5, 'seconds') } - const start = moment(lastValue.checked_at).add(1, 'second') - const end = moment(start).add(59, 'seconds') + const lastBlockInDB = await getTimestampBlock('last') + const diff = end.diff(moment(new Date(lastBlockInDB)), 'seconds') - if (_checkDateGap(lastValue.last_block_at, end)) { - await sleepFor(moment(lastValue.last_block_at).diff(end, 'seconds')) + if (diff >= 0) { + await sleepFor(diff) syncTPSAllTimeHigh() return @@ -351,6 +337,8 @@ const syncTPSAllTimeHigh = async () => { SELECT interval.value as datetime, sum(block_history.transactions_length) as transactions_count, + avg(block_history.cpu_usage) as cpu_usage, + avg(block_history.net_usage) as net_usage, array_to_string(array_agg(block_history.block_num), ',') as blocks FROM interval @@ -361,27 +349,20 @@ const syncTPSAllTimeHigh = async () => { ORDER BY 2 DESC ) - SELECT datetime, transactions_count::integer, blocks FROM tps LIMIT 1 + SELECT datetime, transactions_count::integer, cpu_usage::numeric(5,2), net_usage::numeric(6,3), blocks FROM tps LIMIT 1 `) - if (!rows.length) { - await udpateStats({ - tps_all_time_high: { - ...lastValue, - checked_at: end.toISOString() - } - }) - syncTPSAllTimeHigh() - - return - } - const newValue = rows[0] - if (parseInt(newValue.transactions_count) < lastValue.transactions_count) { + if ( + newValue && + (!lastValue || + parseInt(newValue.transactions_count) >= lastValue.transactions_count) + ) { await udpateStats({ tps_all_time_high: { - ...lastValue, + ...newValue, + blocks: newValue.blocks.split(','), checked_at: end.toISOString() } }) @@ -390,20 +371,10 @@ const syncTPSAllTimeHigh = async () => { return } - const blocks = newValue.blocks.split(',') - - for (let index = 0; index < blocks.length; index++) { - const block = await getBlockUsage(blocks[index]) - - blocks[index] = block - } - await udpateStats({ tps_all_time_high: { - ...newValue, - checked_at: end.toISOString(), - transactions_count: parseInt(newValue.transactions_count), - blocks + ...lastValue, + checked_at: end.toISOString() } }) syncTPSAllTimeHigh() @@ -423,27 +394,41 @@ const sync = async () => { await insertStats(payload) } -const syncTransactionsInfo = async () => { - const transactionsInLastWeek = await getTransactionsInTimeRage( - moment().subtract(1, 'week'), +const getTransactionsStats = async (range) => { + const transactionsStats = await getTransactionsInTimeRage( + moment().subtract(1, range), moment() ) - const payload = { - transactions_in_last_hour: await getTransactionsInTimeRage( - moment().subtract(1, 'hour'), - moment() - ), - transactions_in_last_day: await getTransactionsInTimeRage( - moment().subtract(1, 'day'), - moment() - ), - transactions_in_last_week: transactionsInLastWeek, - average_daily_transactions_in_last_week: transactionsInLastWeek / 7 + + return { + [`transactions_in_last_${range}`]: + transactionsStats?.transactions_count || 0, + [`average_cpu_usage_in_last_${range}`]: transactionsStats?.cpu_usage || 0, + [`average_net_usage_in_last_${range}`]: transactionsStats?.net_usage || 0 } +} + +const syncTransactionsInfo = async () => { + const ranges = ['day', 'hour', 'week'] + let payload + + for (const range of ranges) { + const stats = await getTransactionsStats(range) + + payload = { + ...payload, + ...stats + } + } + + payload.average_daily_transactions_in_last_week = + payload.transactions_in_last_day / 7 || 0 + const stats = await getStats() if (stats) { await udpateStats(payload) + return } await insertStats(payload) diff --git a/hapi/src/workers/producers.worker.js b/hapi/src/workers/producers.worker.js index c1a595a8..b0a12fe4 100644 --- a/hapi/src/workers/producers.worker.js +++ b/hapi/src/workers/producers.worker.js @@ -72,9 +72,9 @@ const start = async () => { if (eosConfig.missedBlocksServiceEnabled) { run('SYNC MISSED BLOCKS', missedBlocksService.syncMissedBlocks) run('SYNC MISSED BLOCKS PER PRODUCER', statsService.getCurrentMissedBlock) + run('SYNC SCHEDULE HISTORY', demuxService.init) } - run('SYNC SCHEDULE HISTORY', demuxService.init) run('SYNC TPS', statsService.syncTPSAllTimeHigh) run( 'SYNC TRANSACTIONS INFO', diff --git a/hasura/metadata/databases/default/tables/public_stat.yaml b/hasura/metadata/databases/default/tables/public_stat.yaml index 3d13f9ed..7480b40d 100644 --- a/hasura/metadata/databases/default/tables/public_stat.yaml +++ b/hasura/metadata/databases/default/tables/public_stat.yaml @@ -1,11 +1,17 @@ table: - schema: public name: stat + schema: public select_permissions: - role: guest permission: columns: + - average_cpu_usage_in_last_day + - average_cpu_usage_in_last_hour + - average_cpu_usage_in_last_week - average_daily_transactions_in_last_week + - average_net_usage_in_last_day + - average_net_usage_in_last_hour + - average_net_usage_in_last_week - created_at - id - missed_blocks diff --git a/hasura/migrations/default/1689088734649_add_average_cpu_and_net_utilization_stats/down.sql b/hasura/migrations/default/1689088734649_add_average_cpu_and_net_utilization_stats/down.sql new file mode 100644 index 00000000..a0e7ec5f --- /dev/null +++ b/hasura/migrations/default/1689088734649_add_average_cpu_and_net_utilization_stats/down.sql @@ -0,0 +1,9 @@ +-- Could not auto-generate a down migration. +-- Please write an appropriate down migration for the SQL below: +-- alter table "public"."stat" add column "average_cpu_usage_in_last_hour" numeric +-- null default '0', add column "average_cpu_usage_in_last_day" numeric +-- null default '0', add column "average_cpu_usage_in_last_week" numeric +-- null default '0', add column "average_net_usage_in_last_hour" numeric +-- null default '0', add column "average_net_usage_in_last_day" numeric +-- null default '0', add column "average_net_usage_in_last_week" numeric +-- null default '0'; diff --git a/hasura/migrations/default/1689088734649_add_average_cpu_and_net_utilization_stats/up.sql b/hasura/migrations/default/1689088734649_add_average_cpu_and_net_utilization_stats/up.sql new file mode 100644 index 00000000..3d5e0012 --- /dev/null +++ b/hasura/migrations/default/1689088734649_add_average_cpu_and_net_utilization_stats/up.sql @@ -0,0 +1,7 @@ +alter table "public"."stat" add column "average_cpu_usage_in_last_hour" numeric + null default '0', add column "average_cpu_usage_in_last_day" numeric + null default '0', add column "average_cpu_usage_in_last_week" numeric + null default '0', add column "average_net_usage_in_last_hour" numeric + null default '0', add column "average_net_usage_in_last_day" numeric + null default '0', add column "average_net_usage_in_last_week" numeric + null default '0'; diff --git a/webapp/src/components/TransactionsHistory/index.js b/webapp/src/components/TransactionsHistory/index.js index 55c877bb..fc124f95 100644 --- a/webapp/src/components/TransactionsHistory/index.js +++ b/webapp/src/components/TransactionsHistory/index.js @@ -1,5 +1,5 @@ /* eslint camelcase: 0 */ -import React, { memo, useEffect, useState } from 'react' +import React, { memo } from 'react' import { makeStyles } from '@mui/styles' import PropTypes from 'prop-types' import Typography from '@mui/material/Typography' @@ -49,22 +49,6 @@ const TransactionsHistory = ({ t, nodesChildren }) => { const classes = useStyles() const [{ info, tps }] = useSharedState() const { data, loading } = useSubscription(BLOCK_TRANSACTIONS_HISTORY) - const [ - blockWithHighestTransactionsCount, - setBlockWithHighestTransactionsCount, - ] = useState({}) - - useEffect(() => { - if (!data?.stats?.[0]?.tps_all_time_high?.blocks?.length) { - return - } - - const blockWithHighestTransactionsCount = - data.stats[0].tps_all_time_high.blocks.sort((first, second) => - first.transactions_count > second.transactions_count ? -1 : 1, - )[0] - setBlockWithHighestTransactionsCount(blockWithHighestTransactionsCount) - }, [data]) return (
@@ -76,18 +60,30 @@ const TransactionsHistory = ({ t, nodesChildren }) => { value={data?.stats[0]?.tps_all_time_high?.transactions_count} loading={loading} classes={classes} - href={getBlockNumUrl(blockWithHighestTransactionsCount.block_num)} + href={getBlockNumUrl( + data?.stats?.[0]?.tps_all_time_high?.blocks[0], + )} + /> + + + {t('cpuUtilizationAllTimeHigh')} + {t('networkUtilizationAllTimeHigh')} @@ -130,6 +126,48 @@ const TransactionsHistory = ({ t, nodesChildren }) => { loading={loading} /> + + {`${t('cpuUtilization')} ${t('lastHour')}`} + + + + {`${t('cpuUtilization')} ${t('lastDay')}`} + + + + {`${t('cpuUtilization')} ${t('lastWeek')}`} + + + + {`${t('netUtilization')} ${t('lastHour')}`} + + + + {`${t('netUtilization')} ${t('lastDay')}`} + + + + {`${t('netUtilization')} ${t('lastWeek')}`} + + )} {nodesChildren && ( diff --git a/webapp/src/context/state.context.js b/webapp/src/context/state.context.js index fad4fa7a..256aac63 100644 --- a/webapp/src/context/state.context.js +++ b/webapp/src/context/state.context.js @@ -77,8 +77,8 @@ const sharedStateReducer = (state, action) => { blocks: [previousBlock.blocks[0], action.payload.blocks[0]], transactions: previousBlock.transactions + action.payload.transactions, - cpu: action.payload.cpu + previousBlock.cpu, - net: action.payload.net + previousBlock.net, + cpu: (action.payload.cpu + previousBlock.cpu) / 2, + net: (action.payload.net + previousBlock.net) / 2, }, ...tps, ], @@ -185,17 +185,11 @@ export const useSharedState = () => { const getGlobalConfig = async () => { if (!global) { try { - const { rows } = await eosApi.getTableRows({ - code: 'eosio', - scope: 'eosio', - table: 'global', - json: true, - lower_bound: null, - }) + const info = await eosApi.getInfo({}) global = { - maxBlockCPU: rows[0]?.max_block_cpu_usage, - maxBlockNET: rows[0]?.max_block_net_usage, + maxBlockCPU: info?.block_cpu_limit, + maxBlockNET: info?.block_net_limit, } } catch (error) {} } diff --git a/webapp/src/gql/producer.gql.js b/webapp/src/gql/producer.gql.js index 7d522f64..71ec5d34 100644 --- a/webapp/src/gql/producer.gql.js +++ b/webapp/src/gql/producer.gql.js @@ -127,6 +127,12 @@ export const BLOCK_TRANSACTIONS_HISTORY = gql` transactions_in_last_day transactions_in_last_week average_daily_transactions_in_last_week + average_cpu_usage_in_last_day + average_net_usage_in_last_day + average_cpu_usage_in_last_hour + average_net_usage_in_last_hour + average_cpu_usage_in_last_week + average_net_usage_in_last_week tps_all_time_high unique_locations missed_blocks diff --git a/webapp/src/language/en.json b/webapp/src/language/en.json index d93e393f..ad171e7e 100644 --- a/webapp/src/language/en.json +++ b/webapp/src/language/en.json @@ -137,7 +137,10 @@ "scheduleVersion": "Schedule Version", "secondsAgo": "Seconds Ago", "tpsAllTimeHigh": "TPS All Time High", - "networkUtilizationAllTimeHigh": "Network Utilization All Time High", + "cpuUtilization": "CPU Usage", + "netUtilization": "NET Usage", + "cpuUtilizationAllTimeHigh": "CPU Usage All Time High", + "networkUtilizationAllTimeHigh": "NET Usage All Time High", "transactionsPerSecond": "Transactions per Second", "transactionsPerBlock": "Transactions per Block", "nodes": "Nodes", diff --git a/webapp/src/language/es.json b/webapp/src/language/es.json index 1766b62f..c834bdd4 100644 --- a/webapp/src/language/es.json +++ b/webapp/src/language/es.json @@ -140,8 +140,11 @@ "Last Week": "Última Semana", "Last Year": "Último Año", "scheduleVersion": "Versión de Programa", - "secondsAgo": "Hace Segundos", - "networkUtilizationAllTimeHigh": "Máxima utilización de la red", + "secondsAgo": "Hace Segundos", + "cpuUtilization": "Uso de CPU", + "netUtilization": "Uso de NET", + "cpuUtilizationAllTimeHigh": "Máxima uso de CPU", + "networkUtilizationAllTimeHigh": "Máxima uso de NET", "tpsAllTimeHigh": "TPS máximo histórico", "transactionsPerSecond": "Transacciones por segundo", "transactionsPerBlock": "Transacciones por bloque", @@ -156,7 +159,7 @@ "transactions": "Transacciones", "uniqueLocations": "Ubicaciones únicas", "cpuLimitPerBlock": "Límite de CPU por bloque", - "netLimitPerBlock": "Límite Red por bloque", + "netLimitPerBlock": "Límite NET por bloque", "chainCpuLimit": "Límite de CPU en cadena", "chainNetLimit": "Límite de Net en cadena", "timeToFinality": "Tiempo para finalidad", diff --git a/webapp/src/routes/Home/TransactionInfo.js b/webapp/src/routes/Home/TransactionInfo.js index efcfccfd..c879c3b7 100644 --- a/webapp/src/routes/Home/TransactionInfo.js +++ b/webapp/src/routes/Home/TransactionInfo.js @@ -126,6 +126,8 @@ const TransactionInfo = ({ t, startTrackingInfo, stopTrackingInfo }) => { }) history.trxPerSecond.push({ + cpu: transactionHistory.cpu / 2 || 0, + net: transactionHistory.net / 2 || 0, y: transactionHistory.transactions_count * 2 || 0, x: new Date(transactionHistory.datetime).getTime(), })