Compare commits
10 Commits
natsoni/bl
...
natsoni/im
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
428cc9eacd | ||
|
|
638acffbad | ||
|
|
ae9125d316 | ||
|
|
07fd3d3409 | ||
|
|
f7360433a1 | ||
|
|
f6fac92180 | ||
|
|
82d1502bfa | ||
|
|
8ab104d191 | ||
|
|
263742132c | ||
|
|
fad39e0bea |
@@ -7,7 +7,7 @@ import cpfpRepository from '../repositories/CpfpRepository';
|
||||
import { RowDataPacket } from 'mysql2';
|
||||
|
||||
class DatabaseMigration {
|
||||
private static currentVersion = 82;
|
||||
private static currentVersion = 83;
|
||||
private queryTimeout = 3600_000;
|
||||
private statisticsAddedIndexed = false;
|
||||
private uniqueLogs: string[] = [];
|
||||
@@ -705,6 +705,11 @@ class DatabaseMigration {
|
||||
await this.$fixBadV1AuditBlocks();
|
||||
await this.updateToSchemaVersion(82);
|
||||
}
|
||||
|
||||
if (databaseSchemaVersion < 83) {
|
||||
await this.$addPerSecondVsizeToStatistics();
|
||||
await this.updateToSchemaVersion(83);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1341,6 +1346,47 @@ class DatabaseMigration {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async $addPerSecondVsizeToStatistics(): Promise<void> {
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_2` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_3` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_4` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_5` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_6` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_8` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_10` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_12` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_15` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_20` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_30` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_40` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_50` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_60` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_70` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_80` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_90` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_100` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_125` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_150` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_175` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_200` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_250` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_300` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_350` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_400` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_500` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_600` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_700` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_800` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_900` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1000` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1200` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1400` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1600` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_1800` int(11) NOT NULL DEFAULT 0');
|
||||
await this.$executeQuery('ALTER TABLE `statistics` ADD `vsize_ps_2000` int(11) NOT NULL DEFAULT 0');
|
||||
}
|
||||
}
|
||||
|
||||
export default new DatabaseMigration();
|
||||
|
||||
@@ -12,6 +12,7 @@ import rbfCache from './rbf-cache';
|
||||
import { Acceleration } from './services/acceleration';
|
||||
import redisCache from './redis-cache';
|
||||
import blocks from './blocks';
|
||||
import { logFees } from './statistics/statistics';
|
||||
|
||||
class Mempool {
|
||||
private inSync: boolean = false;
|
||||
@@ -34,6 +35,7 @@ class Mempool {
|
||||
|
||||
private vBytesPerSecondArray: VbytesPerSecond[] = [];
|
||||
private vBytesPerSecond: number = 0;
|
||||
private vBytesPerSecondByFeeRate: { [feePerWU: number]: number } = {};
|
||||
private mempoolProtection = 0;
|
||||
private latestTransactions: any[] = [];
|
||||
|
||||
@@ -193,6 +195,10 @@ class Mempool {
|
||||
return this.vBytesPerSecond;
|
||||
}
|
||||
|
||||
public getVBytesPerSecondByFeeRate(): { [feePerWU: number]: number } {
|
||||
return this.vBytesPerSecondByFeeRate;
|
||||
}
|
||||
|
||||
public getFirstSeenForTransactions(txIds: string[]): number[] {
|
||||
const txTimes: number[] = [];
|
||||
txIds.forEach((txId: string) => {
|
||||
@@ -270,6 +276,7 @@ class Mempool {
|
||||
this.vBytesPerSecondArray.push({
|
||||
unixTime: new Date().getTime(),
|
||||
vSize: transaction.vsize,
|
||||
effectiveFeePerVsize: transaction.effectiveFeePerVsize
|
||||
});
|
||||
}
|
||||
hasChange = true;
|
||||
@@ -588,6 +595,21 @@ class Mempool {
|
||||
this.vBytesPerSecond = Math.round(
|
||||
this.vBytesPerSecondArray.map((data) => data.vSize).reduce((a, b) => a + b) / config.STATISTICS.TX_PER_SECOND_SAMPLE_PERIOD
|
||||
);
|
||||
|
||||
if (!Common.isLiquid()) {
|
||||
this.vBytesPerSecondByFeeRate = {};
|
||||
for (const tx of this.vBytesPerSecondArray) {
|
||||
for (let i = 0; i < logFees.length; i++) {
|
||||
if (tx.effectiveFeePerVsize < logFees[i + 1] || i === logFees.length - 1) {
|
||||
this.vBytesPerSecondByFeeRate[logFees[i]] = (this.vBytesPerSecondByFeeRate[logFees[i]] || 0) + tx.vSize;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const feeRate of Object.keys(this.vBytesPerSecondByFeeRate)) {
|
||||
this.vBytesPerSecondByFeeRate[feeRate] = Math.round(this.vBytesPerSecondByFeeRate[feeRate] / config.STATISTICS.TX_PER_SECOND_SAMPLE_PERIOD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ class MiningRoutes {
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/fee-rates/:interval', this.$getHistoricalBlockFeeRates)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/sizes-weights/:interval', this.$getHistoricalBlockSizeAndWeight)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/difficulty-adjustments/:interval', this.$getDifficultyAdjustments)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/difficulty-adjustment/:height', this.$getDifficultyAdjustment)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/predictions/:interval', this.$getHistoricalBlocksHealth)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/audit/scores', this.$getBlockAuditScores)
|
||||
.get(config.MEMPOOL.API_URL_PREFIX + 'mining/blocks/audit/scores/:height', this.$getBlockAuditScores)
|
||||
@@ -298,18 +297,6 @@ class MiningRoutes {
|
||||
}
|
||||
}
|
||||
|
||||
private async $getDifficultyAdjustment(req: Request, res: Response) {
|
||||
try {
|
||||
const adjustment = await DifficultyAdjustmentsRepository.$getAdjustmentAtHeight(parseInt(req.params.height, 10));
|
||||
res.header('Pragma', 'public');
|
||||
res.header('Cache-control', 'public');
|
||||
res.setHeader('Expires', new Date(Date.now() + 1000 * 300).toUTCString());
|
||||
res.json(adjustment);
|
||||
} catch (e) {
|
||||
res.status(e instanceof Error && e.message === 'not found' ? 204 : 500).send(e instanceof Error ? e.message : e);
|
||||
}
|
||||
}
|
||||
|
||||
private async $getRewardStats(req: Request, res: Response) {
|
||||
try {
|
||||
const response = await mining.$getRewardStats(parseInt(req.params.blockCount, 10));
|
||||
|
||||
@@ -53,7 +53,45 @@ class StatisticsApi {
|
||||
vsize_1400,
|
||||
vsize_1600,
|
||||
vsize_1800,
|
||||
vsize_2000
|
||||
vsize_2000,
|
||||
vsize_ps_1,
|
||||
vsize_ps_2,
|
||||
vsize_ps_3,
|
||||
vsize_ps_4,
|
||||
vsize_ps_5,
|
||||
vsize_ps_6,
|
||||
vsize_ps_8,
|
||||
vsize_ps_10,
|
||||
vsize_ps_12,
|
||||
vsize_ps_15,
|
||||
vsize_ps_20,
|
||||
vsize_ps_30,
|
||||
vsize_ps_40,
|
||||
vsize_ps_50,
|
||||
vsize_ps_60,
|
||||
vsize_ps_70,
|
||||
vsize_ps_80,
|
||||
vsize_ps_90,
|
||||
vsize_ps_100,
|
||||
vsize_ps_125,
|
||||
vsize_ps_150,
|
||||
vsize_ps_175,
|
||||
vsize_ps_200,
|
||||
vsize_ps_250,
|
||||
vsize_ps_300,
|
||||
vsize_ps_350,
|
||||
vsize_ps_400,
|
||||
vsize_ps_500,
|
||||
vsize_ps_600,
|
||||
vsize_ps_700,
|
||||
vsize_ps_800,
|
||||
vsize_ps_900,
|
||||
vsize_ps_1000,
|
||||
vsize_ps_1200,
|
||||
vsize_ps_1400,
|
||||
vsize_ps_1600,
|
||||
vsize_ps_1800,
|
||||
vsize_ps_2000
|
||||
)
|
||||
VALUES (NOW(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)`;
|
||||
@@ -67,56 +105,94 @@ class StatisticsApi {
|
||||
public async $create(statistics: Statistic, convertToDatetime = false): Promise<number | undefined> {
|
||||
try {
|
||||
const query = `INSERT INTO statistics(
|
||||
added,
|
||||
unconfirmed_transactions,
|
||||
tx_per_second,
|
||||
vbytes_per_second,
|
||||
mempool_byte_weight,
|
||||
fee_data,
|
||||
total_fee,
|
||||
min_fee,
|
||||
vsize_1,
|
||||
vsize_2,
|
||||
vsize_3,
|
||||
vsize_4,
|
||||
vsize_5,
|
||||
vsize_6,
|
||||
vsize_8,
|
||||
vsize_10,
|
||||
vsize_12,
|
||||
vsize_15,
|
||||
vsize_20,
|
||||
vsize_30,
|
||||
vsize_40,
|
||||
vsize_50,
|
||||
vsize_60,
|
||||
vsize_70,
|
||||
vsize_80,
|
||||
vsize_90,
|
||||
vsize_100,
|
||||
vsize_125,
|
||||
vsize_150,
|
||||
vsize_175,
|
||||
vsize_200,
|
||||
vsize_250,
|
||||
vsize_300,
|
||||
vsize_350,
|
||||
vsize_400,
|
||||
vsize_500,
|
||||
vsize_600,
|
||||
vsize_700,
|
||||
vsize_800,
|
||||
vsize_900,
|
||||
vsize_1000,
|
||||
vsize_1200,
|
||||
vsize_1400,
|
||||
vsize_1600,
|
||||
vsize_1800,
|
||||
vsize_2000
|
||||
)
|
||||
VALUES (${convertToDatetime ? `FROM_UNIXTIME(${statistics.added})` : statistics.added}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
added,
|
||||
unconfirmed_transactions,
|
||||
tx_per_second,
|
||||
vbytes_per_second,
|
||||
mempool_byte_weight,
|
||||
fee_data,
|
||||
total_fee,
|
||||
min_fee,
|
||||
vsize_1,
|
||||
vsize_2,
|
||||
vsize_3,
|
||||
vsize_4,
|
||||
vsize_5,
|
||||
vsize_6,
|
||||
vsize_8,
|
||||
vsize_10,
|
||||
vsize_12,
|
||||
vsize_15,
|
||||
vsize_20,
|
||||
vsize_30,
|
||||
vsize_40,
|
||||
vsize_50,
|
||||
vsize_60,
|
||||
vsize_70,
|
||||
vsize_80,
|
||||
vsize_90,
|
||||
vsize_100,
|
||||
vsize_125,
|
||||
vsize_150,
|
||||
vsize_175,
|
||||
vsize_200,
|
||||
vsize_250,
|
||||
vsize_300,
|
||||
vsize_350,
|
||||
vsize_400,
|
||||
vsize_500,
|
||||
vsize_600,
|
||||
vsize_700,
|
||||
vsize_800,
|
||||
vsize_900,
|
||||
vsize_1000,
|
||||
vsize_1200,
|
||||
vsize_1400,
|
||||
vsize_1600,
|
||||
vsize_1800,
|
||||
vsize_2000,
|
||||
vsize_ps_1,
|
||||
vsize_ps_2,
|
||||
vsize_ps_3,
|
||||
vsize_ps_4,
|
||||
vsize_ps_5,
|
||||
vsize_ps_6,
|
||||
vsize_ps_8,
|
||||
vsize_ps_10,
|
||||
vsize_ps_12,
|
||||
vsize_ps_15,
|
||||
vsize_ps_20,
|
||||
vsize_ps_30,
|
||||
vsize_ps_40,
|
||||
vsize_ps_50,
|
||||
vsize_ps_60,
|
||||
vsize_ps_70,
|
||||
vsize_ps_80,
|
||||
vsize_ps_90,
|
||||
vsize_ps_100,
|
||||
vsize_ps_125,
|
||||
vsize_ps_150,
|
||||
vsize_ps_175,
|
||||
vsize_ps_200,
|
||||
vsize_ps_250,
|
||||
vsize_ps_300,
|
||||
vsize_ps_350,
|
||||
vsize_ps_400,
|
||||
vsize_ps_500,
|
||||
vsize_ps_600,
|
||||
vsize_ps_700,
|
||||
vsize_ps_800,
|
||||
vsize_ps_900,
|
||||
vsize_ps_1000,
|
||||
vsize_ps_1200,
|
||||
vsize_ps_1400,
|
||||
vsize_ps_1600,
|
||||
vsize_ps_1800,
|
||||
vsize_ps_2000
|
||||
)
|
||||
VALUES (${convertToDatetime ? `FROM_UNIXTIME(${statistics.added})` : statistics.added}, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
|
||||
const params: (string | number)[] = [
|
||||
statistics.unconfirmed_transactions,
|
||||
statistics.tx_per_second,
|
||||
@@ -163,6 +239,44 @@ class StatisticsApi {
|
||||
statistics.vsize_1600,
|
||||
statistics.vsize_1800,
|
||||
statistics.vsize_2000,
|
||||
statistics.vsize_ps_1,
|
||||
statistics.vsize_ps_2,
|
||||
statistics.vsize_ps_3,
|
||||
statistics.vsize_ps_4,
|
||||
statistics.vsize_ps_5,
|
||||
statistics.vsize_ps_6,
|
||||
statistics.vsize_ps_8,
|
||||
statistics.vsize_ps_10,
|
||||
statistics.vsize_ps_12,
|
||||
statistics.vsize_ps_15,
|
||||
statistics.vsize_ps_20,
|
||||
statistics.vsize_ps_30,
|
||||
statistics.vsize_ps_40,
|
||||
statistics.vsize_ps_50,
|
||||
statistics.vsize_ps_60,
|
||||
statistics.vsize_ps_70,
|
||||
statistics.vsize_ps_80,
|
||||
statistics.vsize_ps_90,
|
||||
statistics.vsize_ps_100,
|
||||
statistics.vsize_ps_125,
|
||||
statistics.vsize_ps_150,
|
||||
statistics.vsize_ps_175,
|
||||
statistics.vsize_ps_200,
|
||||
statistics.vsize_ps_250,
|
||||
statistics.vsize_ps_300,
|
||||
statistics.vsize_ps_350,
|
||||
statistics.vsize_ps_400,
|
||||
statistics.vsize_ps_500,
|
||||
statistics.vsize_ps_600,
|
||||
statistics.vsize_ps_700,
|
||||
statistics.vsize_ps_800,
|
||||
statistics.vsize_ps_900,
|
||||
statistics.vsize_ps_1000,
|
||||
statistics.vsize_ps_1200,
|
||||
statistics.vsize_ps_1400,
|
||||
statistics.vsize_ps_1600,
|
||||
statistics.vsize_ps_1800,
|
||||
statistics.vsize_ps_2000,
|
||||
];
|
||||
const [result]: any = await DB.query(query, params);
|
||||
return result.insertId;
|
||||
@@ -214,7 +328,45 @@ class StatisticsApi {
|
||||
CAST(avg(vsize_1400) as DOUBLE) as vsize_1400,
|
||||
CAST(avg(vsize_1600) as DOUBLE) as vsize_1600,
|
||||
CAST(avg(vsize_1800) as DOUBLE) as vsize_1800,
|
||||
CAST(avg(vsize_2000) as DOUBLE) as vsize_2000 \
|
||||
CAST(avg(vsize_2000) as DOUBLE) as vsize_2000,
|
||||
CAST(avg(vsize_ps_1) as DOUBLE) as vsize_ps_1,
|
||||
CAST(avg(vsize_ps_2) as DOUBLE) as vsize_ps_2,
|
||||
CAST(avg(vsize_ps_3) as DOUBLE) as vsize_ps_3,
|
||||
CAST(avg(vsize_ps_4) as DOUBLE) as vsize_ps_4,
|
||||
CAST(avg(vsize_ps_5) as DOUBLE) as vsize_ps_5,
|
||||
CAST(avg(vsize_ps_6) as DOUBLE) as vsize_ps_6,
|
||||
CAST(avg(vsize_ps_8) as DOUBLE) as vsize_ps_8,
|
||||
CAST(avg(vsize_ps_10) as DOUBLE) as vsize_ps_10,
|
||||
CAST(avg(vsize_ps_12) as DOUBLE) as vsize_ps_12,
|
||||
CAST(avg(vsize_ps_15) as DOUBLE) as vsize_ps_15,
|
||||
CAST(avg(vsize_ps_20) as DOUBLE) as vsize_ps_20,
|
||||
CAST(avg(vsize_ps_30) as DOUBLE) as vsize_ps_30,
|
||||
CAST(avg(vsize_ps_40) as DOUBLE) as vsize_ps_40,
|
||||
CAST(avg(vsize_ps_50) as DOUBLE) as vsize_ps_50,
|
||||
CAST(avg(vsize_ps_60) as DOUBLE) as vsize_ps_60,
|
||||
CAST(avg(vsize_ps_70) as DOUBLE) as vsize_ps_70,
|
||||
CAST(avg(vsize_ps_80) as DOUBLE) as vsize_ps_80,
|
||||
CAST(avg(vsize_ps_90) as DOUBLE) as vsize_ps_90,
|
||||
CAST(avg(vsize_ps_100) as DOUBLE) as vsize_ps_100,
|
||||
CAST(avg(vsize_ps_125) as DOUBLE) as vsize_ps_125,
|
||||
CAST(avg(vsize_ps_150) as DOUBLE) as vsize_ps_150,
|
||||
CAST(avg(vsize_ps_175) as DOUBLE) as vsize_ps_175,
|
||||
CAST(avg(vsize_ps_200) as DOUBLE) as vsize_ps_200,
|
||||
CAST(avg(vsize_ps_250) as DOUBLE) as vsize_ps_250,
|
||||
CAST(avg(vsize_ps_300) as DOUBLE) as vsize_ps_300,
|
||||
CAST(avg(vsize_ps_350) as DOUBLE) as vsize_ps_350,
|
||||
CAST(avg(vsize_ps_400) as DOUBLE) as vsize_ps_400,
|
||||
CAST(avg(vsize_ps_500) as DOUBLE) as vsize_ps_500,
|
||||
CAST(avg(vsize_ps_600) as DOUBLE) as vsize_ps_600,
|
||||
CAST(avg(vsize_ps_700) as DOUBLE) as vsize_ps_700,
|
||||
CAST(avg(vsize_ps_800) as DOUBLE) as vsize_ps_800,
|
||||
CAST(avg(vsize_ps_900) as DOUBLE) as vsize_ps_900,
|
||||
CAST(avg(vsize_ps_1000) as DOUBLE) as vsize_ps_1000,
|
||||
CAST(avg(vsize_ps_1200) as DOUBLE) as vsize_ps_1200,
|
||||
CAST(avg(vsize_ps_1400) as DOUBLE) as vsize_ps_1400,
|
||||
CAST(avg(vsize_ps_1600) as DOUBLE) as vsize_ps_1600,
|
||||
CAST(avg(vsize_ps_1800) as DOUBLE) as vsize_ps_1800,
|
||||
CAST(avg(vsize_ps_2000) as DOUBLE) as vsize_ps_2000 \
|
||||
FROM statistics \
|
||||
${interval === 'all' ? '' : `WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`} \
|
||||
GROUP BY UNIX_TIMESTAMP(added) DIV ${div} \
|
||||
@@ -264,7 +416,45 @@ class StatisticsApi {
|
||||
vsize_1400,
|
||||
vsize_1600,
|
||||
vsize_1800,
|
||||
vsize_2000 \
|
||||
vsize_2000,
|
||||
vsize_ps_1,
|
||||
vsize_ps_2,
|
||||
vsize_ps_3,
|
||||
vsize_ps_4,
|
||||
vsize_ps_5,
|
||||
vsize_ps_6,
|
||||
vsize_ps_8,
|
||||
vsize_ps_10,
|
||||
vsize_ps_12,
|
||||
vsize_ps_15,
|
||||
vsize_ps_20,
|
||||
vsize_ps_30,
|
||||
vsize_ps_40,
|
||||
vsize_ps_50,
|
||||
vsize_ps_60,
|
||||
vsize_ps_70,
|
||||
vsize_ps_80,
|
||||
vsize_ps_90,
|
||||
vsize_ps_100,
|
||||
vsize_ps_125,
|
||||
vsize_ps_150,
|
||||
vsize_ps_175,
|
||||
vsize_ps_200,
|
||||
vsize_ps_250,
|
||||
vsize_ps_300,
|
||||
vsize_ps_350,
|
||||
vsize_ps_400,
|
||||
vsize_ps_500,
|
||||
vsize_ps_600,
|
||||
vsize_ps_700,
|
||||
vsize_ps_800,
|
||||
vsize_ps_900,
|
||||
vsize_ps_1000,
|
||||
vsize_ps_1200,
|
||||
vsize_ps_1400,
|
||||
vsize_ps_1600,
|
||||
vsize_ps_1800,
|
||||
vsize_ps_2000 \
|
||||
FROM statistics \
|
||||
${interval === 'all' ? '' : `WHERE added BETWEEN DATE_SUB(NOW(), INTERVAL ${interval}) AND NOW()`} \
|
||||
GROUP BY UNIX_TIMESTAMP(added) DIV ${div} \
|
||||
@@ -452,7 +642,47 @@ class StatisticsApi {
|
||||
s.vsize_1600,
|
||||
s.vsize_1800,
|
||||
s.vsize_2000,
|
||||
]
|
||||
],
|
||||
vsizes_ps: [
|
||||
s.vsize_ps_1,
|
||||
s.vsize_ps_2,
|
||||
s.vsize_ps_3,
|
||||
s.vsize_ps_4,
|
||||
s.vsize_ps_5,
|
||||
s.vsize_ps_6,
|
||||
s.vsize_ps_8,
|
||||
s.vsize_ps_10,
|
||||
s.vsize_ps_12,
|
||||
s.vsize_ps_15,
|
||||
s.vsize_ps_20,
|
||||
s.vsize_ps_30,
|
||||
s.vsize_ps_40,
|
||||
s.vsize_ps_50,
|
||||
s.vsize_ps_60,
|
||||
s.vsize_ps_70,
|
||||
s.vsize_ps_80,
|
||||
s.vsize_ps_90,
|
||||
s.vsize_ps_100,
|
||||
s.vsize_ps_125,
|
||||
s.vsize_ps_150,
|
||||
s.vsize_ps_175,
|
||||
s.vsize_ps_200,
|
||||
s.vsize_ps_250,
|
||||
s.vsize_ps_300,
|
||||
s.vsize_ps_350,
|
||||
s.vsize_ps_400,
|
||||
s.vsize_ps_500,
|
||||
s.vsize_ps_600,
|
||||
s.vsize_ps_700,
|
||||
s.vsize_ps_800,
|
||||
s.vsize_ps_900,
|
||||
s.vsize_ps_1000,
|
||||
s.vsize_ps_1200,
|
||||
s.vsize_ps_1400,
|
||||
s.vsize_ps_1600,
|
||||
s.vsize_ps_1800,
|
||||
s.vsize_ps_2000,
|
||||
],
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -506,6 +736,44 @@ class StatisticsApi {
|
||||
vsize_1600: s.vsizes[35],
|
||||
vsize_1800: s.vsizes[36],
|
||||
vsize_2000: s.vsizes[37],
|
||||
vsize_ps_1: s.vsizes_ps?.[0] || 0,
|
||||
vsize_ps_2: s.vsizes_ps?.[1] || 0,
|
||||
vsize_ps_3: s.vsizes_ps?.[2] || 0,
|
||||
vsize_ps_4: s.vsizes_ps?.[3] || 0,
|
||||
vsize_ps_5: s.vsizes_ps?.[4] || 0,
|
||||
vsize_ps_6: s.vsizes_ps?.[5] || 0,
|
||||
vsize_ps_8: s.vsizes_ps?.[6] || 0,
|
||||
vsize_ps_10: s.vsizes_ps?.[7] || 0,
|
||||
vsize_ps_12: s.vsizes_ps?.[8] || 0,
|
||||
vsize_ps_15: s.vsizes_ps?.[9] || 0,
|
||||
vsize_ps_20: s.vsizes_ps?.[10] || 0,
|
||||
vsize_ps_30: s.vsizes_ps?.[11] || 0,
|
||||
vsize_ps_40: s.vsizes_ps?.[12] || 0,
|
||||
vsize_ps_50: s.vsizes_ps?.[13] || 0,
|
||||
vsize_ps_60: s.vsizes_ps?.[14] || 0,
|
||||
vsize_ps_70: s.vsizes_ps?.[15] || 0,
|
||||
vsize_ps_80: s.vsizes_ps?.[16] || 0,
|
||||
vsize_ps_90: s.vsizes_ps?.[17] || 0,
|
||||
vsize_ps_100: s.vsizes_ps?.[18] || 0,
|
||||
vsize_ps_125: s.vsizes_ps?.[19] || 0,
|
||||
vsize_ps_150: s.vsizes_ps?.[20] || 0,
|
||||
vsize_ps_175: s.vsizes_ps?.[21] || 0,
|
||||
vsize_ps_200: s.vsizes_ps?.[22] || 0,
|
||||
vsize_ps_250: s.vsizes_ps?.[23] || 0,
|
||||
vsize_ps_300: s.vsizes_ps?.[24] || 0,
|
||||
vsize_ps_350: s.vsizes_ps?.[25] || 0,
|
||||
vsize_ps_400: s.vsizes_ps?.[26] || 0,
|
||||
vsize_ps_500: s.vsizes_ps?.[27] || 0,
|
||||
vsize_ps_600: s.vsizes_ps?.[28] || 0,
|
||||
vsize_ps_700: s.vsizes_ps?.[29] || 0,
|
||||
vsize_ps_800: s.vsizes_ps?.[30] || 0,
|
||||
vsize_ps_900: s.vsizes_ps?.[31] || 0,
|
||||
vsize_ps_1000: s.vsizes_ps?.[32] || 0,
|
||||
vsize_ps_1200: s.vsizes_ps?.[33] || 0,
|
||||
vsize_ps_1400: s.vsizes_ps?.[34] || 0,
|
||||
vsize_ps_1600: s.vsizes_ps?.[35] || 0,
|
||||
vsize_ps_1800: s.vsizes_ps?.[36] || 0,
|
||||
vsize_ps_2000: s.vsizes_ps?.[37] || 0,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import { TransactionExtended, OptimizedStatistic } from '../../mempool.interface
|
||||
import { Common } from '../common';
|
||||
import statisticsApi from './statistics-api';
|
||||
|
||||
export const logFees = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
||||
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
||||
|
||||
class Statistics {
|
||||
protected intervalTimer: NodeJS.Timer | undefined;
|
||||
protected lastRun: number = 0;
|
||||
@@ -42,6 +45,7 @@ class Statistics {
|
||||
const currentMempool = memPool.getMempool();
|
||||
const txPerSecond = memPool.getTxPerSecond();
|
||||
const vBytesPerSecond = memPool.getVBytesPerSecond();
|
||||
const vBytesPerSecondByFeeRate = memPool.getVBytesPerSecondByFeeRate();
|
||||
|
||||
logger.debug('Running statistics');
|
||||
|
||||
@@ -73,9 +77,6 @@ class Statistics {
|
||||
const totalWeight = memPoolArray.map((tx) => tx.vsize).reduce((acc, curr) => acc + curr) * 4;
|
||||
const totalFee = memPoolArray.map((tx) => tx.fee).reduce((acc, curr) => acc + curr);
|
||||
|
||||
const logFees = [1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 175, 200,
|
||||
250, 300, 350, 400, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000];
|
||||
|
||||
const weightVsizeFees: { [feePerWU: number]: number } = {};
|
||||
const lastItem = logFees.length - 1;
|
||||
|
||||
@@ -147,6 +148,44 @@ class Statistics {
|
||||
vsize_1600: weightVsizeFees['1600'] || 0,
|
||||
vsize_1800: weightVsizeFees['1800'] || 0,
|
||||
vsize_2000: weightVsizeFees['2000'] || 0,
|
||||
vsize_ps_1: vBytesPerSecondByFeeRate['1'] || 0,
|
||||
vsize_ps_2: vBytesPerSecondByFeeRate['2'] || 0,
|
||||
vsize_ps_3: vBytesPerSecondByFeeRate['3'] || 0,
|
||||
vsize_ps_4: vBytesPerSecondByFeeRate['4'] || 0,
|
||||
vsize_ps_5: vBytesPerSecondByFeeRate['5'] || 0,
|
||||
vsize_ps_6: vBytesPerSecondByFeeRate['6'] || 0,
|
||||
vsize_ps_8: vBytesPerSecondByFeeRate['8'] || 0,
|
||||
vsize_ps_10: vBytesPerSecondByFeeRate['10'] || 0,
|
||||
vsize_ps_12: vBytesPerSecondByFeeRate['12'] || 0,
|
||||
vsize_ps_15: vBytesPerSecondByFeeRate['15'] || 0,
|
||||
vsize_ps_20: vBytesPerSecondByFeeRate['20'] || 0,
|
||||
vsize_ps_30: vBytesPerSecondByFeeRate['30'] || 0,
|
||||
vsize_ps_40: vBytesPerSecondByFeeRate['40'] || 0,
|
||||
vsize_ps_50: vBytesPerSecondByFeeRate['50'] || 0,
|
||||
vsize_ps_60: vBytesPerSecondByFeeRate['60'] || 0,
|
||||
vsize_ps_70: vBytesPerSecondByFeeRate['70'] || 0,
|
||||
vsize_ps_80: vBytesPerSecondByFeeRate['80'] || 0,
|
||||
vsize_ps_90: vBytesPerSecondByFeeRate['90'] || 0,
|
||||
vsize_ps_100: vBytesPerSecondByFeeRate['100'] || 0,
|
||||
vsize_ps_125: vBytesPerSecondByFeeRate['125'] || 0,
|
||||
vsize_ps_150: vBytesPerSecondByFeeRate['150'] || 0,
|
||||
vsize_ps_175: vBytesPerSecondByFeeRate['175'] || 0,
|
||||
vsize_ps_200: vBytesPerSecondByFeeRate['200'] || 0,
|
||||
vsize_ps_250: vBytesPerSecondByFeeRate['250'] || 0,
|
||||
vsize_ps_300: vBytesPerSecondByFeeRate['300'] || 0,
|
||||
vsize_ps_350: vBytesPerSecondByFeeRate['350'] || 0,
|
||||
vsize_ps_400: vBytesPerSecondByFeeRate['400'] || 0,
|
||||
vsize_ps_500: vBytesPerSecondByFeeRate['500'] || 0,
|
||||
vsize_ps_600: vBytesPerSecondByFeeRate['600'] || 0,
|
||||
vsize_ps_700: vBytesPerSecondByFeeRate['700'] || 0,
|
||||
vsize_ps_800: vBytesPerSecondByFeeRate['800'] || 0,
|
||||
vsize_ps_900: vBytesPerSecondByFeeRate['900'] || 0,
|
||||
vsize_ps_1000: vBytesPerSecondByFeeRate['1000'] || 0,
|
||||
vsize_ps_1200: vBytesPerSecondByFeeRate['1200'] || 0,
|
||||
vsize_ps_1400: vBytesPerSecondByFeeRate['1400'] || 0,
|
||||
vsize_ps_1600: vBytesPerSecondByFeeRate['1600'] || 0,
|
||||
vsize_ps_1800: vBytesPerSecondByFeeRate['1800'] || 0,
|
||||
vsize_ps_2000: vBytesPerSecondByFeeRate['2000'] || 0,
|
||||
});
|
||||
|
||||
if (this.newStatisticsEntryCallback && insertId) {
|
||||
|
||||
@@ -439,6 +439,46 @@ export interface Statistic {
|
||||
vsize_1600: number;
|
||||
vsize_1800: number;
|
||||
vsize_2000: number;
|
||||
|
||||
vsize_ps_1: number;
|
||||
vsize_ps_2: number;
|
||||
vsize_ps_3: number;
|
||||
vsize_ps_4: number;
|
||||
vsize_ps_5: number;
|
||||
vsize_ps_6: number;
|
||||
vsize_ps_8: number;
|
||||
vsize_ps_10: number;
|
||||
vsize_ps_12: number;
|
||||
vsize_ps_15: number;
|
||||
vsize_ps_20: number;
|
||||
vsize_ps_30: number;
|
||||
vsize_ps_40: number;
|
||||
vsize_ps_50: number;
|
||||
vsize_ps_60: number;
|
||||
vsize_ps_70: number;
|
||||
vsize_ps_80: number;
|
||||
vsize_ps_90: number;
|
||||
vsize_ps_100: number;
|
||||
vsize_ps_125: number;
|
||||
vsize_ps_150: number;
|
||||
vsize_ps_175: number;
|
||||
vsize_ps_200: number;
|
||||
vsize_ps_250: number;
|
||||
vsize_ps_300: number;
|
||||
vsize_ps_350: number;
|
||||
vsize_ps_400: number;
|
||||
vsize_ps_500: number;
|
||||
vsize_ps_600: number;
|
||||
vsize_ps_700: number;
|
||||
vsize_ps_800: number;
|
||||
vsize_ps_900: number;
|
||||
vsize_ps_1000: number;
|
||||
vsize_ps_1200: number;
|
||||
vsize_ps_1400: number;
|
||||
vsize_ps_1600: number;
|
||||
vsize_ps_1800: number;
|
||||
vsize_ps_2000: number;
|
||||
|
||||
}
|
||||
|
||||
export interface OptimizedStatistic {
|
||||
@@ -449,6 +489,7 @@ export interface OptimizedStatistic {
|
||||
mempool_byte_weight: number;
|
||||
min_fee: number;
|
||||
vsizes: number[];
|
||||
vsizes_ps: number[];
|
||||
}
|
||||
|
||||
export interface TxTrackingInfo {
|
||||
@@ -481,6 +522,7 @@ export interface WebsocketResponse {
|
||||
export interface VbytesPerSecond {
|
||||
unixTime: number;
|
||||
vSize: number;
|
||||
effectiveFeePerVsize: number;
|
||||
}
|
||||
|
||||
export interface RequiredSpec { [name: string]: RequiredParams; }
|
||||
|
||||
@@ -88,22 +88,6 @@ class DifficultyAdjustmentsRepository {
|
||||
}
|
||||
}
|
||||
|
||||
public async $getAdjustmentAtHeight(height: number): Promise<IndexedDifficultyAdjustment> {
|
||||
try {
|
||||
if (isNaN(height)) {
|
||||
throw new Error(`argument must be a number`);
|
||||
}
|
||||
const [rows] = await DB.query(`SELECT * FROM difficulty_adjustments WHERE height = ?`, [height]);
|
||||
if (!rows[0]) {
|
||||
throw new Error(`not found`);
|
||||
}
|
||||
return rows[0] as IndexedDifficultyAdjustment;
|
||||
} catch (e: any) {
|
||||
logger.err(`Cannot get difficulty adjustment from the database. Reason: ${e instanceof Error ? e.message : e}`, logger.tags.mining);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
public async $getAdjustmentsHeights(): Promise<number[]> {
|
||||
try {
|
||||
const [rows]: any[] = await DB.query(`SELECT height FROM difficulty_adjustments`);
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<span>Spiral</span>
|
||||
</a>
|
||||
<a href="https://foundrydigital.com/" target="_blank" title="Foundry">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="b" data-name="Layer 2" style="zoom: 1;" width="32" height="76" viewBox="0 0 32 76" class="image">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="b" data-name="Layer 2" style="zoom: 1;" width="32" height="90" viewBox="0 -5 32 90" class="image">
|
||||
<defs>
|
||||
<style>
|
||||
.d {
|
||||
@@ -130,14 +130,9 @@
|
||||
</svg>
|
||||
<span>Unchained</span>
|
||||
</a>
|
||||
<a href="https://gemini.com/" target="_blank" title="Gemini">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 0 360 360" class="image">
|
||||
<rect style="fill: black" width="360" height="360" />
|
||||
<g transform="matrix(0.62 0 0 0.62 180 180)">
|
||||
<path style="fill: rgb(0,220,250)" transform=" translate(-162, -162)" d="M 211.74 0 C 154.74 0 106.35 43.84 100.25 100.25 C 43.84 106.35 1.4210854715202004e-14 154.76 1.4210854715202004e-14 211.74 C 0.044122601308501076 273.7212006364817 50.27879936351834 323.95587739869154 112.26 324 C 169.26 324 217.84 280.15999999999997 223.75 223.75 C 280.15999999999997 217.65 324 169.24 324 112.26 C 323.95587739869154 50.278799363518324 273.72120063648174 0.04412260130848722 211.74 -1.4210854715202004e-14 z M 297.74 124.84 C 291.9644950552469 162.621439649343 262.2969457716857 192.26062994820046 224.51 198 L 224.51 124.84 z M 26.3 199.16 C 31.986912917108594 161.30935034910615 61.653433460549415 131.56986937804106 99.48999999999998 125.78999999999999 L 99.49 199 L 26.3 199 z M 198.21 224.51 C 191.87736076583954 267.0991541201681 155.312384597087 298.62923417787493 112.255 298.62923417787493 C 69.19761540291302 298.62923417787493 32.63263923416048 267.0991541201682 26.3 224.51 z M 199.16 124.83999999999999 L 199.16 199 L 124.84 199 L 124.84 124.84 z M 297.7 99.48999999999998 L 125.78999999999999 99.48999999999998 C 132.12263923416046 56.90084587983182 168.687615402913 25.37076582212505 211.745 25.37076582212505 C 254.80238459708698 25.37076582212505 291.3673607658395 56.900845879831834 297.7 99.49 z" stroke-linecap="round" />
|
||||
</g>
|
||||
</svg>
|
||||
<span>Gemini</span>
|
||||
<a href="https://bitkey.world/" target="_blank" title="Bitkey">
|
||||
<img class="image" src="/resources/profile/bitkey.svg" />
|
||||
<span>Bitkey</span>
|
||||
</a>
|
||||
<a href="https://bullbitcoin.com/" target="_blank" title="Bull Bitcoin">
|
||||
<svg aria-hidden="true" class="image" viewBox="0 -5 40 40" xmlns="http://www.w3.org/2000/svg">
|
||||
@@ -193,6 +188,19 @@
|
||||
</svg>
|
||||
<span>Exodus</span>
|
||||
</a>
|
||||
<a href="https://gemini.com/" target="_blank" title="Gemini">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="360" height="360" viewBox="0 0 360 360" class="image">
|
||||
<rect style="fill: black" width="360" height="360" />
|
||||
<g transform="matrix(0.62 0 0 0.62 180 180)">
|
||||
<path style="fill: rgb(0,220,250)" transform=" translate(-162, -162)" d="M 211.74 0 C 154.74 0 106.35 43.84 100.25 100.25 C 43.84 106.35 1.4210854715202004e-14 154.76 1.4210854715202004e-14 211.74 C 0.044122601308501076 273.7212006364817 50.27879936351834 323.95587739869154 112.26 324 C 169.26 324 217.84 280.15999999999997 223.75 223.75 C 280.15999999999997 217.65 324 169.24 324 112.26 C 323.95587739869154 50.278799363518324 273.72120063648174 0.04412260130848722 211.74 -1.4210854715202004e-14 z M 297.74 124.84 C 291.9644950552469 162.621439649343 262.2969457716857 192.26062994820046 224.51 198 L 224.51 124.84 z M 26.3 199.16 C 31.986912917108594 161.30935034910615 61.653433460549415 131.56986937804106 99.48999999999998 125.78999999999999 L 99.49 199 L 26.3 199 z M 198.21 224.51 C 191.87736076583954 267.0991541201681 155.312384597087 298.62923417787493 112.255 298.62923417787493 C 69.19761540291302 298.62923417787493 32.63263923416048 267.0991541201682 26.3 224.51 z M 199.16 124.83999999999999 L 199.16 199 L 124.84 199 L 124.84 124.84 z M 297.7 99.48999999999998 L 125.78999999999999 99.48999999999998 C 132.12263923416046 56.90084587983182 168.687615402913 25.37076582212505 211.745 25.37076582212505 C 254.80238459708698 25.37076582212505 291.3673607658395 56.900845879831834 297.7 99.49 z" stroke-linecap="round" />
|
||||
</g>
|
||||
</svg>
|
||||
<span>Gemini</span>
|
||||
</a>
|
||||
<a href="https://leather.io/" target="_blank" title="Leather">
|
||||
<img class="image" src="/resources/profile/leather.svg" />
|
||||
<span>Leather</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -251,3 +251,12 @@
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.enterprise-sponsor {
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
max-width: 800px;
|
||||
}
|
||||
}
|
||||
@@ -78,25 +78,6 @@
|
||||
</ng-template>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="this.stateService.network === '' && block.height % 2016 === 0">
|
||||
<td i18n="mining.difficulty-adjustment">Adjustment</td>
|
||||
<td>
|
||||
<ng-container *ngIf="cacheService.daCache[block.height]?.adjustment > 0; else loadingAdjustment">
|
||||
<div [style.color]="cacheService.daCache[block.height].adjustment > 1 ? 'var(--green)' : (cacheService.daCache[block.height].adjustment < 1 ? 'var(--red)' : '')">
|
||||
@if (cacheService.daCache[block.height].adjustment > 1) {
|
||||
<fa-icon class="retarget-sign up" [icon]="['fas', 'caret-up']" [fixedWidth]="true"></fa-icon>
|
||||
} @else if (cacheService.daCache[block.height].adjustment < 1) {
|
||||
<fa-icon class="retarget-sign" [icon]="['fas', 'caret-down']" [fixedWidth]="true"></fa-icon>
|
||||
}
|
||||
{{ (cacheService.daCache[block.height].adjustment - 1) * 100 | absolute | number: '1.2-2' }}
|
||||
<span class="symbol">%</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #loadingAdjustment>
|
||||
<span class="skeleton-loader" style="max-width: 60px"></span>
|
||||
</ng-template>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template #skeletonRows>
|
||||
<tr>
|
||||
@@ -212,10 +193,6 @@
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="this.stateService.network === '' && block.height % 2016 === 0">
|
||||
<td i18n="block.difficulty">Difficulty</td>
|
||||
<td>{{ block.difficulty | amountShortener: 2 }}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<ng-template #loadingRest>
|
||||
<tr>
|
||||
|
||||
@@ -280,11 +280,3 @@ h1 {
|
||||
top: -1px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.retarget-sign {
|
||||
margin-right: -3px;
|
||||
&.up {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
private relativeUrlPipe: RelativeUrlPipe,
|
||||
private apiService: ApiService,
|
||||
private priceService: PriceService,
|
||||
public cacheService: CacheService,
|
||||
private cacheService: CacheService,
|
||||
private servicesApiService: ServicesApiServices,
|
||||
private cd: ChangeDetectorRef,
|
||||
private preloadService: PreloadService,
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Observable, Subscription, delay, filter, of, retryWhen, switchMap, take, tap, throwError } from 'rxjs';
|
||||
import { Observable, Subscription, delay, filter, tap } from 'rxjs';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { specialBlocks } from '../../app.constants';
|
||||
import { BlockExtended } from '../../interfaces/node-api.interface';
|
||||
import { Location } from '@angular/common';
|
||||
import { CacheService } from '../../services/cache.service';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { colorFromRetarget } from '../../shared/common.utils';
|
||||
|
||||
interface BlockchainBlock extends BlockExtended {
|
||||
placeholder?: boolean;
|
||||
@@ -79,7 +77,6 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
constructor(
|
||||
public stateService: StateService,
|
||||
public cacheService: CacheService,
|
||||
public apiService: ApiService,
|
||||
private cd: ChangeDetectorRef,
|
||||
private location: Location,
|
||||
) {
|
||||
@@ -337,31 +334,6 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
return this.specialBlocks[height]?.networks.includes(this.stateService.network || 'mainnet') ? true : false;
|
||||
}
|
||||
|
||||
isDA(height: number): boolean {
|
||||
const isDA = height % 2016 === 0 && this.stateService.network === '';
|
||||
if (isDA && !this.cacheService.daCache[height]?.exact) {
|
||||
const estimatedAdjustment = this.cacheService.daCache[height]?.adjustment || 0;
|
||||
this.cacheService.daCache[height] = { adjustment: estimatedAdjustment, exact: true };
|
||||
this.apiService.getDifficultyAdjustmentByHeight$(height).pipe(
|
||||
switchMap(da => {
|
||||
const blocksAvailable = (this.height || this.chainTip) && this.blockStyles[(this.height || this.chainTip) - height];
|
||||
return blocksAvailable ? of(da) : throwError(() => new Error());
|
||||
}),
|
||||
retryWhen(errors =>
|
||||
errors.pipe(
|
||||
delay(1000),
|
||||
take(3)
|
||||
)
|
||||
),
|
||||
tap((da) => {
|
||||
this.cacheService.daCache[height] = { adjustment: da?.adjustment || 1, exact: true };
|
||||
this.blockStyles[(this.height || this.chainTip) - height].background = colorFromRetarget(da?.adjustment);
|
||||
})
|
||||
).subscribe();
|
||||
}
|
||||
return isDA;
|
||||
}
|
||||
|
||||
getStyleForBlock(block: BlockchainBlock, index: number, animateEnterFrom: number = 0) {
|
||||
if (!block || block.placeholder) {
|
||||
return this.getStyleForPlaceholderBlock(index, animateEnterFrom);
|
||||
@@ -377,8 +349,7 @@ export class BlockchainBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
return {
|
||||
left: addLeft + this.blockOffset * index + 'px',
|
||||
background: this.isDA(block.height) ? colorFromRetarget(this.cacheService.daCache[block.height]?.adjustment || 1) :
|
||||
`repeating-linear-gradient(
|
||||
background: `repeating-linear-gradient(
|
||||
var(--secondary),
|
||||
var(--secondary) ${greenBackgroundHeight}%,
|
||||
${this.gradientColors[this.network][0]} ${Math.max(greenBackgroundHeight, 0)}%,
|
||||
|
||||
@@ -5,7 +5,6 @@ import { ApiService } from '../../services/api.service';
|
||||
import { formatNumber } from '@angular/common';
|
||||
import { selectPowerOfTen } from '../../bitcoin.utils';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { CacheService } from '../../services/cache.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-difficulty-adjustments-table',
|
||||
@@ -28,8 +27,7 @@ export class DifficultyAdjustmentsTable implements OnInit {
|
||||
constructor(
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
private apiService: ApiService,
|
||||
public stateService: StateService,
|
||||
private cacheService: CacheService,
|
||||
public stateService: StateService
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -54,7 +52,6 @@ export class DifficultyAdjustmentsTable implements OnInit {
|
||||
adjustment[2] / selectedPowerOfTen.divider,
|
||||
this.locale, `1.${decimals}-${decimals}`) + selectedPowerOfTen.unit
|
||||
});
|
||||
this.cacheService.daCache[adjustment[1]] = { adjustment: adjustment[3], exact: true };
|
||||
}
|
||||
this.isLoading = false;
|
||||
return tableData.slice(0, 6);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</div>
|
||||
|
||||
<div class="faucet-container text-center">
|
||||
|
||||
|
||||
@if (txid) {
|
||||
<div class="alert alert-success w-100 text-truncate">
|
||||
<fa-icon [icon]="['fas', 'circle-check']"></fa-icon>
|
||||
@@ -36,6 +36,13 @@
|
||||
<app-twitter-login customClass="btn btn-sm" width="180px" redirectTo="/testnet4/faucet" buttonString="Link your Twitter"></app-twitter-login>
|
||||
</div>
|
||||
}
|
||||
@else if (error === 'account_limited') {
|
||||
<div class="alert alert-mempool d-block text-center w-100">
|
||||
<div class="d-inline align-middle">
|
||||
<span class="mb-2 mr-2">Your Twitter account does not allow you to access the faucet</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@else if (error) {
|
||||
<!-- User can request -->
|
||||
<app-mempool-error class="w-100" [error]="error"></app-mempool-error>
|
||||
@@ -81,7 +88,7 @@
|
||||
}
|
||||
|
||||
<!-- Send back coins -->
|
||||
@if (status?.address) {
|
||||
@if (status?.address) {
|
||||
<div class="mt-4 alert alert-info w-100">If you no longer need your testnet4 coins, please consider <a class="text-primary" [routerLink]="['/address/' | relativeUrl, status.address]"><u>sending them back</u></a> to replenish the faucet.</div>
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export class FaucetComponent implements OnInit, OnDestroy {
|
||||
error: string = '';
|
||||
user: any = undefined;
|
||||
txid: string = '';
|
||||
|
||||
|
||||
faucetStatusSubscription: Subscription;
|
||||
status: {
|
||||
min: number; // minimum amount to request at once (in sats)
|
||||
|
||||
@@ -41,25 +41,6 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr *ngIf="this.stateService.network === '' && mempoolBlock.height % 2016 === 0">
|
||||
<td i18n="mining.difficulty-adjustment">Adjustment</td>
|
||||
<td>
|
||||
<ng-container *ngIf="cacheService.daCache[mempoolBlock.height]?.adjustment > 0; else loadingAdjustment">
|
||||
<div [style.color]="cacheService.daCache[mempoolBlock.height].adjustment > 1 ? 'var(--green)' : (cacheService.daCache[mempoolBlock.height].adjustment < 1 ? 'var(--red)' : '')">
|
||||
@if (cacheService.daCache[mempoolBlock.height].adjustment > 1) {
|
||||
<fa-icon class="retarget-sign up" [icon]="['fas', 'caret-up']" [fixedWidth]="true"></fa-icon>
|
||||
} @else if (cacheService.daCache[mempoolBlock.height].adjustment < 1) {
|
||||
<fa-icon class="retarget-sign" [icon]="['fas', 'caret-down']" [fixedWidth]="true"></fa-icon>
|
||||
}
|
||||
{{ (cacheService.daCache[mempoolBlock.height].adjustment - 1) * 100 | absolute | number: '1.2-2' }}
|
||||
<span class="symbol">%</span>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-template #loadingAdjustment>
|
||||
<span class="skeleton-loader" style="max-width: 60px"></span>
|
||||
</ng-template>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<app-fee-distribution-graph *ngIf="webGlEnabled" [transactions]="mempoolBlockTransactions$ | async" [feeRange]="mempoolBlock.isStack ? mempoolBlock.feeRange : []" [vsize]="mempoolBlock.blockVSize" ></app-fee-distribution-graph>
|
||||
|
||||
@@ -36,11 +36,3 @@ h1 {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.retarget-sign {
|
||||
margin-right: -3px;
|
||||
&.up {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { Observable, BehaviorSubject } from 'rxjs';
|
||||
import { SeoService } from '../../services/seo.service';
|
||||
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
||||
import { WebsocketService } from '../../services/websocket.service';
|
||||
import { CacheService } from '../../services/cache.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-mempool-block',
|
||||
@@ -31,7 +30,6 @@ export class MempoolBlockComponent implements OnInit, OnDestroy {
|
||||
public stateService: StateService,
|
||||
private seoService: SeoService,
|
||||
private websocketService: WebsocketService,
|
||||
public cacheService: CacheService,
|
||||
private cd: ChangeDetectorRef,
|
||||
@Inject(PLATFORM_ID) private platformId: Object,
|
||||
) {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, ChangeDetectorRef, HostListener, Input, OnChanges, SimpleChanges, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
|
||||
import { Subscription, Observable, of, combineLatest, throwError } from 'rxjs';
|
||||
import { Subscription, Observable, of, combineLatest } from 'rxjs';
|
||||
import { MempoolBlock } from '../../interfaces/websocket.interface';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { EtaService } from '../../services/eta.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { delay, filter, map, retryWhen, switchMap, take, tap } from 'rxjs/operators';
|
||||
import { delay, filter, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { feeLevels } from '../../app.constants';
|
||||
import { specialBlocks } from '../../app.constants';
|
||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||
@@ -12,8 +12,6 @@ import { Location } from '@angular/common';
|
||||
import { DifficultyAdjustment, MempoolPosition } from '../../interfaces/node-api.interface';
|
||||
import { animate, style, transition, trigger } from '@angular/animations';
|
||||
import { ThemeService } from '../../services/theme.service';
|
||||
import { CacheService } from '../../services/cache.service';
|
||||
import { colorFromRetarget } from '../../shared/common.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-mempool-blocks',
|
||||
@@ -95,7 +93,6 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
constructor(
|
||||
private router: Router,
|
||||
public stateService: StateService,
|
||||
public cacheService: CacheService,
|
||||
private etaService: EtaService,
|
||||
private themeService: ThemeService,
|
||||
private cd: ChangeDetectorRef,
|
||||
@@ -390,37 +387,6 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.mempoolBlocksFull.forEach((block, i) => this.mempoolBlockStyles.push(this.getStyleForMempoolBlock(block, i)));
|
||||
}
|
||||
|
||||
isDA(height: number): boolean {
|
||||
if (this.chainTip === -1) {
|
||||
return false;
|
||||
}
|
||||
const isDA = height % 2016 === 0 && this.stateService.network === '';
|
||||
if (isDA && !this.cacheService.daCache[height]) {
|
||||
this.cacheService.daCache[height] = { adjustment: 0 };
|
||||
this.difficultyAdjustments$.pipe(
|
||||
filter(da => !!da),
|
||||
switchMap(da => {
|
||||
const mempoolBlocksAvailable = this.chainTip && this.mempoolBlockStyles[height - this.chainTip - 1];
|
||||
return mempoolBlocksAvailable ? of(da) : throwError(() => new Error());
|
||||
}),
|
||||
retryWhen(errors =>
|
||||
errors.pipe(
|
||||
delay(100),
|
||||
take(3)
|
||||
)
|
||||
),
|
||||
tap(da => {
|
||||
const adjustment = parseFloat((1 + da.difficultyChange / 100).toFixed(4));
|
||||
if (adjustment !== this.cacheService.daCache[height].adjustment) {
|
||||
this.cacheService.daCache[height].adjustment = adjustment;
|
||||
this.mempoolBlockStyles[height - this.chainTip - 1].background = colorFromRetarget(adjustment);
|
||||
}
|
||||
})
|
||||
).subscribe();
|
||||
}
|
||||
return isDA;
|
||||
}
|
||||
|
||||
getStyleForMempoolBlock(mempoolBlock: MempoolBlock, index: number) {
|
||||
const emptyBackgroundSpacePercentage = Math.max(100 - mempoolBlock.blockVSize / this.stateService.blockVSize * 100, 0);
|
||||
const usedBlockSpace = 100 - emptyBackgroundSpacePercentage;
|
||||
@@ -444,7 +410,7 @@ export class MempoolBlocksComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
return {
|
||||
'right': this.containerOffset + index * this.blockOffset + 'px',
|
||||
'background': this.isDA(mempoolBlock.height) ? colorFromRetarget(this.cacheService.daCache[mempoolBlock.height]?.adjustment || 1) : backgroundGradients.join(',') + ')'
|
||||
'background': backgroundGradients.join(',') + ')'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import { StorageService } from '../../services/storage.service';
|
||||
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
||||
import { getTransactionFlags, getUnacceleratedFeeRate } from '../../shared/transaction.utils';
|
||||
import { Filter, TransactionFlags, toFilters } from '../../shared/filters.utils';
|
||||
import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment, Acceleration, AccelerationPosition } from '../../interfaces/node-api.interface';
|
||||
import { BlockExtended, CpfpInfo, RbfTree, MempoolPosition, DifficultyAdjustment, Acceleration, AccelerationPosition, OptimizedMempoolStats } from '../../interfaces/node-api.interface';
|
||||
import { LiquidUnblinding } from './liquid-ublinding';
|
||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||
import { PriceService } from '../../services/price.service';
|
||||
@@ -139,6 +139,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
firstLoad = true;
|
||||
waitingForAccelerationInfo: boolean = false;
|
||||
isLoadingFirstSeen = false;
|
||||
mempoolStats: OptimizedMempoolStats[] = null;
|
||||
|
||||
featuresEnabled: boolean;
|
||||
segwitEnabled: boolean;
|
||||
@@ -196,7 +197,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
this.websocketService.want(['blocks', 'mempool-blocks']);
|
||||
this.websocketService.want(['blocks', 'mempool-blocks', 'live-2h-chart']);
|
||||
this.stateService.networkChanged$.subscribe(
|
||||
(network) => {
|
||||
this.network = network;
|
||||
@@ -769,8 +770,20 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.stateService.difficultyAdjustment$.pipe(startWith(null)),
|
||||
this.isAccelerated$,
|
||||
this.txChanged$,
|
||||
this.apiService.list2HStatistics$(),
|
||||
this.stateService.live2Chart$.pipe(startWith(null)),
|
||||
]).pipe(
|
||||
map(([position, mempoolBlocks, da, isAccelerated]) => {
|
||||
map(([position, mempoolBlocks, da, isAccelerated, _, mempoolStats, mempoolStat]) => {
|
||||
|
||||
if (this.mempoolStats === null) {
|
||||
this.mempoolStats = mempoolStats;
|
||||
}
|
||||
|
||||
if (this.mempoolStats.length && mempoolStat && this.mempoolStats[0].added !== mempoolStat.added) {
|
||||
this.mempoolStats.pop();
|
||||
this.mempoolStats.unshift(mempoolStat);
|
||||
}
|
||||
|
||||
return this.etaService.calculateETA(
|
||||
this.network,
|
||||
this.tx,
|
||||
@@ -780,6 +793,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.miningStats,
|
||||
isAccelerated,
|
||||
this.accelerationPositions,
|
||||
this.mempoolStats,
|
||||
);
|
||||
})
|
||||
);
|
||||
@@ -977,6 +991,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.isAcceleration = false;
|
||||
this.isAccelerated$.next(this.isAcceleration);
|
||||
this.eligibleForAcceleration = false;
|
||||
this.mempoolStats = null;
|
||||
this.leaveTransaction();
|
||||
}
|
||||
|
||||
|
||||
@@ -3670,39 +3670,6 @@ export const restApiDocsData = [
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "mining",
|
||||
httpRequestMethod: "GET",
|
||||
fragment: "get-difficulty-adjustment-by-height",
|
||||
title: "GET Difficulty Adjustment",
|
||||
description: {
|
||||
default: "<p>Returns difficulty adjustment for the block at the specified <code>:blockHeight</code>. If no adjustment happened at that height, an empty response is returned.</p>"
|
||||
},
|
||||
urlString: "/v1/mining/difficulty-adjustment/:blockHeight",
|
||||
showConditions: [""],
|
||||
showJsExamples: showJsExamplesDefaultFalse,
|
||||
codeExample: {
|
||||
default: {
|
||||
codeTemplate: {
|
||||
curl: `/api/v1/mining/difficulty-adjustment/%{1}`,
|
||||
commonJS: ``,
|
||||
esModule: ``
|
||||
},
|
||||
codeSampleMainnet: {
|
||||
esModule: [],
|
||||
commonJS: [],
|
||||
curl: [`756000`],
|
||||
response: `{
|
||||
"time": "2022-09-28T02:56:34.000Z",
|
||||
"height": 756000,
|
||||
"difficulty": 31360548173144.9,
|
||||
"adjustment": 0.97863
|
||||
}`
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "endpoint",
|
||||
category: "mining",
|
||||
|
||||
@@ -7,6 +7,7 @@ export interface OptimizedMempoolStats {
|
||||
total_fee: number;
|
||||
mempool_byte_weight: number;
|
||||
vsizes: number[];
|
||||
vsizes_ps: number[];
|
||||
}
|
||||
|
||||
interface Ancestor {
|
||||
|
||||
@@ -315,12 +315,6 @@ export class ApiService {
|
||||
);
|
||||
}
|
||||
|
||||
getDifficultyAdjustmentByHeight$(height: number): Observable<any> {
|
||||
return this.httpClient.get<any>(
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/difficulty-adjustment/${height}`
|
||||
);
|
||||
}
|
||||
|
||||
getHistoricalHashrate$(interval: string | undefined): Observable<any> {
|
||||
return this.httpClient.get<any[]>(
|
||||
this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/hashrate` +
|
||||
|
||||
@@ -23,7 +23,6 @@ export class CacheService {
|
||||
blockLoading: { [height: number]: boolean } = {};
|
||||
copiesInBlockQueue: { [height: number]: number } = {};
|
||||
blockPriorities: number[] = [];
|
||||
daCache: { [height: number]: { adjustment: number, exact?: boolean } } = {};
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
@@ -129,7 +128,6 @@ export class CacheService {
|
||||
this.blockLoading = {};
|
||||
this.copiesInBlockQueue = {};
|
||||
this.blockPriorities = [];
|
||||
this.daCache = {};
|
||||
}
|
||||
|
||||
getCachedBlock(height) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AccelerationPosition, CpfpInfo, DifficultyAdjustment, MempoolPosition, SinglePoolStats } from '../interfaces/node-api.interface';
|
||||
import { AccelerationPosition, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, SinglePoolStats } from '../interfaces/node-api.interface';
|
||||
import { StateService } from './state.service';
|
||||
import { MempoolBlock } from '../interfaces/websocket.interface';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
@@ -7,6 +7,7 @@ import { MiningService, MiningStats } from './mining.service';
|
||||
import { getUnacceleratedFeeRate } from '../shared/transaction.utils';
|
||||
import { AccelerationEstimate } from '../components/accelerate-checkout/accelerate-checkout.component';
|
||||
import { Observable, combineLatest, map, of, share, shareReplay, tap } from 'rxjs';
|
||||
import { feeLevels } from '../app.constants';
|
||||
|
||||
export interface ETA {
|
||||
now: number, // time at which calculation performed
|
||||
@@ -113,6 +114,7 @@ export class EtaService {
|
||||
miningStats: MiningStats,
|
||||
isAccelerated: boolean,
|
||||
accelerationPositions: AccelerationPosition[],
|
||||
mempoolStats: OptimizedMempoolStats[] = [],
|
||||
): ETA | null {
|
||||
// return this.calculateETA(tx, this.accelerationPositions, position, mempoolBlocks, da, isAccelerated)
|
||||
if (!tx || !mempoolBlocks) {
|
||||
@@ -143,7 +145,17 @@ export class EtaService {
|
||||
|
||||
if (!isAccelerated) {
|
||||
const blocks = mempoolPosition.block + 1;
|
||||
const wait = da.adjustedTimeAvg * (mempoolPosition.block + 1);
|
||||
|
||||
const vsizeAhead = mempoolPosition.vsize + this.stateService.blockVSize * mempoolPosition.block;
|
||||
const incomingVsizePerBlock = this.estimateVsizePerSecond(tx, mempoolStats) * da.adjustedTimeAvg / 1000;
|
||||
const vsizeConsumedPerBlock = Math.max(
|
||||
this.stateService.blockVSize - incomingVsizePerBlock,
|
||||
0.05 * this.stateService.blockVSize // So that we don't return infinite ETA
|
||||
)
|
||||
|
||||
const blocksUntilMined = Math.ceil(vsizeAhead / vsizeConsumedPerBlock);
|
||||
const wait = blocksUntilMined * da.adjustedTimeAvg;
|
||||
|
||||
return {
|
||||
now,
|
||||
time: wait + now + da.timeOffset,
|
||||
@@ -279,4 +291,46 @@ export class EtaService {
|
||||
return tx.fee / (tx.weight / 4);
|
||||
|
||||
}
|
||||
|
||||
estimateVsizePerSecond(tx: Transaction, mempoolStats: OptimizedMempoolStats[], timeWindow: number = 15 * 60 * 1000): number {
|
||||
const nowMinusTimeSpan = (new Date().getTime() - timeWindow) / 1000;
|
||||
const vsizeAboveTransaction = mempoolStats
|
||||
// Remove datapoints older than now - timeWindow
|
||||
.filter(stat => stat.added > nowMinusTimeSpan)
|
||||
// Remove datapoints less than 45 seconds apart from the previous one
|
||||
.filter((el, i, arr) => {
|
||||
if (i === 0) {
|
||||
return true;
|
||||
}
|
||||
return arr[i - 1].added - el.added > 45;
|
||||
})
|
||||
// For each datapoint, compute the total vsize of transactions with higher fee rate
|
||||
.map(stat => {
|
||||
let vsizeAbove = 0;
|
||||
for (let i = feeLevels.length - 1; i >= 0; i--) {
|
||||
if (feeLevels[i] > tx.effectiveFeePerVsize) {
|
||||
vsizeAbove += stat.vsizes_ps[i];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return vsizeAbove;
|
||||
});
|
||||
|
||||
// vsizeAboveTransaction is a temporal series of past vsize values above the transaction's fee rate
|
||||
// From this array we need to estimate the future vsize per second
|
||||
// Naive first approach: take the median of the series
|
||||
if (!vsizeAboveTransaction.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const sorted = Array.from(vsizeAboveTransaction).sort((a, b) => a - b);
|
||||
const middle = Math.floor(sorted.length / 2);
|
||||
|
||||
if (sorted.length % 2 === 0) {
|
||||
return (sorted[middle - 1] + sorted[middle]) / 2;
|
||||
}
|
||||
|
||||
return sorted[middle];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,25 +240,3 @@ export function md5(inputString): string {
|
||||
}
|
||||
return rh(a)+rh(b)+rh(c)+rh(d);
|
||||
}
|
||||
|
||||
export function colorFromRetarget(da: number): string {
|
||||
const minDA = 0.95;
|
||||
const maxDA = 1.05;
|
||||
const midDA = 1;
|
||||
|
||||
const red = { r: 220, g: 53, b: 69 };
|
||||
const grey = { r: 108, g: 117, b: 125 };
|
||||
const green = { r: 59, g: 204, b: 73 };
|
||||
|
||||
const interpolateColor = (color1, color2, ratio) => {
|
||||
ratio = Math.min(1, Math.max(0, ratio));
|
||||
const r = Math.round(color1.r + ratio * (color2.r - color1.r));
|
||||
const g = Math.round(color1.g + ratio * (color2.g - color1.g));
|
||||
const b = Math.round(color1.b + ratio * (color2.b - color1.b));
|
||||
return `rgba(${r}, ${g}, ${b}, 0.7)`;
|
||||
}
|
||||
|
||||
return da <= midDA ?
|
||||
interpolateColor(red, grey, (da - minDA) / (midDA - minDA)) :
|
||||
interpolateColor(grey, green, (da - midDA) / (maxDA - midDA));
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { NgbCollapseModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstra
|
||||
import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
|
||||
import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle,
|
||||
faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown,
|
||||
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline, faCircleXmark} from '@fortawesome/free-solid-svg-icons';
|
||||
faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip, faTimeline, faCircleXmark, faCalendarCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
|
||||
import { MenuComponent } from '../components/menu/menu.component';
|
||||
import { PreviewTitleComponent } from '../components/master-page-preview/preview-title.component';
|
||||
@@ -440,5 +440,6 @@ export class SharedModule {
|
||||
library.addIcons(faFaucetDrip);
|
||||
library.addIcons(faTimeline);
|
||||
library.addIcons(faCircleXmark);
|
||||
library.addIcons(faCalendarCheck);
|
||||
}
|
||||
}
|
||||
|
||||
3
frontend/src/resources/profile/bitkey.svg
Normal file
3
frontend/src/resources/profile/bitkey.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="212" height="450" viewBox="0 0 212 450" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M114.005 2.14659C109.052 -0.71553 102.948 -0.715531 97.9948 2.14659L7.99481 54.1531C3.04732 57.012 0 62.2924 0 68.0065V172.043C0 177.758 3.04733 183.038 7.99482 185.897L69.0247 221.163C73.9722 224.022 77.0195 229.302 77.0195 235.016V433.998V437.998C77.0195 444.625 82.3921 449.998 89.0195 449.998H93.0195H118.981H123.031C129.658 449.998 135.031 444.625 135.031 437.998V233.751C135.444 228.531 138.395 223.809 142.975 221.163L204.005 185.897C208.953 183.038 212 177.758 212 172.043V68.0065C212 62.2924 208.953 57.012 204.005 54.1531L114.005 2.14659ZM112.07 68.1162C108.334 65.938 103.716 65.938 99.9803 68.1162L63.9718 89.1129C60.2841 91.2631 58.0164 95.2105 58.0164 99.4793V141.68C58.0164 145.948 60.2841 149.896 63.9718 152.046L99.9803 173.043C103.716 175.221 108.334 175.221 112.07 173.043L148.078 152.046C151.766 149.896 154.034 145.948 154.034 141.68V99.4793C154.034 95.2105 151.766 91.2631 148.078 89.1129L112.07 68.1162ZM172.046 338.527C164.047 333.861 154 339.631 154 348.892L154 356L154 412V419.108C154 428.369 164.047 434.14 172.046 429.473L196.046 415.473C199.733 413.322 202 409.376 202 405.108V362.892C202 358.624 199.733 354.678 196.046 352.527L172.046 338.527Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
3
frontend/src/resources/profile/leather.svg
Normal file
3
frontend/src/resources/profile/leather.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="387" height="377" viewBox="-58.05 -56.55 503.1 490.1" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M249.327 137.966C283.745 132.618 333.52 96.2553 333.52 67.9135C333.52 59.3574 326.637 53.4752 316.576 53.4752C297.513 53.4752 265.212 82.3518 249.327 137.966ZM89.9409 278.606C44.9317 278.606 41.225 323.525 86.2343 323.525C106.356 323.525 130.714 315.504 143.422 301.065C124.889 285.023 109.533 278.606 89.9409 278.606ZM376.411 259.355C379.059 334.755 340.934 377 276.332 377C238.207 377 219.144 362.562 178.371 335.824C157.19 359.353 116.946 377 83.5867 377C-31.3192 377 -26.5536 229.943 90.4704 229.943C114.828 229.943 135.48 236.36 161.956 252.938L179.43 191.441C107.415 171.655 71.4077 116.041 106.886 36.3631H164.074C132.303 89.3035 154.013 133.153 194.256 137.966C215.967 60.4269 262.565 0 324.518 0C359.467 0 387.002 22.9943 387.002 64.705C387.002 131.549 300.161 186.094 234.5 191.441L207.494 287.162C238.207 322.99 323.459 357.749 323.459 259.355H376.411Z" fill="#F5F1ED"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1011 B |
@@ -392,9 +392,9 @@ DEBIAN_UNFURL_PKG+=(libxdamage-dev libxrandr-dev libgbm-dev libpango1.0-dev liba
|
||||
# packages needed for mempool ecosystem
|
||||
FREEBSD_PKG=()
|
||||
FREEBSD_PKG+=(zsh sudo git git-lfs screen curl wget calc neovim)
|
||||
FREEBSD_PKG+=(openssh-portable py39-pip rust llvm10 jq base64 libzmq4)
|
||||
FREEBSD_PKG+=(openssh-portable py311-pip rust llvm18 jq base64 libzmq4)
|
||||
FREEBSD_PKG+=(boost-libs autoconf automake gmake gcc libevent libtool pkgconf)
|
||||
FREEBSD_PKG+=(nginx rsync py39-certbot-nginx mariadb1011-server keybase)
|
||||
FREEBSD_PKG+=(nginx rsync py311-certbot-nginx mariadb1011-server)
|
||||
FREEBSD_PKG+=(geoipupdate redis)
|
||||
|
||||
FREEBSD_UNFURL_PKG=()
|
||||
|
||||
Reference in New Issue
Block a user