mirror of
https://github.com/ditatompel/xmr-remote-nodes.git
synced 2024-11-16 17:07:36 +00:00
Adding table tbl_fee
This table used to store majority fee of monero nettype. By calculating majority fee via "cron" every 300s, the function to get majority fee for nettypes can be done with single query. The frontend majority static data in the frontend removed and now use `/api/v1/fees` endpoint to get majority fee value. Note: Don't know if it works well with `onload` method or not. Let see.
This commit is contained in:
parent
55f6af1f22
commit
48fe09c1cb
6 changed files with 159 additions and 72 deletions
|
@ -6,26 +6,6 @@ export async function load() {
|
||||||
title: 'Public Monero Remote Nodes List',
|
title: 'Public Monero Remote Nodes List',
|
||||||
description: 'List of public Monero remote nodes that you can use with your favourite Monero wallet. You can filter by country, protocol, or CORS capable nodes.',
|
description: 'List of public Monero remote nodes that you can use with your favourite Monero wallet. You can filter by country, protocol, or CORS capable nodes.',
|
||||||
keywords: 'monero remote nodes,public monero nodes,monero public nodes,monero wallet,tor monero node,monero cors rpc'
|
keywords: 'monero remote nodes,public monero nodes,monero public nodes,monero wallet,tor monero node,monero cors rpc'
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Array containing network fees.
|
|
||||||
* For now, I use static data to reduce the amount of API calls.
|
|
||||||
* See the values from `/api/v1/fees`
|
|
||||||
* @type {{ nettype: string, estimate_fee: number }[]}
|
|
||||||
*/
|
|
||||||
netFees: [
|
|
||||||
{
|
|
||||||
nettype: 'mainnet',
|
|
||||||
estimate_fee: 20000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
nettype: 'stagenet',
|
|
||||||
estimate_fee: 56000
|
|
||||||
},
|
|
||||||
{
|
|
||||||
nettype: 'testnet',
|
|
||||||
estimate_fee: 20000
|
|
||||||
}
|
}
|
||||||
]
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { DataHandler } from '@vincjo/datatables/remote';
|
import { DataHandler } from '@vincjo/datatables/remote';
|
||||||
import { format, formatDistance } from 'date-fns';
|
import { format, formatDistance } from 'date-fns';
|
||||||
import { loadData, loadCountries } from './api-handler';
|
import { loadData, loadFees, loadCountries } from './api-handler';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import {
|
import {
|
||||||
DtSrRowsPerPage,
|
DtSrRowsPerPage,
|
||||||
|
@ -30,12 +30,18 @@
|
||||||
|
|
||||||
/** @type {{total_nodes: number, cc: string, name: string}[]} */
|
/** @type {{total_nodes: number, cc: string, name: string}[]} */
|
||||||
let countries = [];
|
let countries = [];
|
||||||
|
let fees = [];
|
||||||
|
|
||||||
const handler = new DataHandler([], { rowsPerPage: 10, totalRows: 0 });
|
const handler = new DataHandler([], { rowsPerPage: 10, totalRows: 0 });
|
||||||
let rows = handler.getRows();
|
let rows = handler.getRows();
|
||||||
|
|
||||||
/** @type {Object.<string, number>} */
|
/** @type {Object.<string, number>} */
|
||||||
let majorityFee = data.netFees.reduce(
|
let majorityFee;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
loadFees().then((data) => {
|
||||||
|
fees = data;
|
||||||
|
majorityFee = fees.reduce(
|
||||||
/**
|
/**
|
||||||
* @param {Object.<string, number>} o
|
* @param {Object.<string, number>} o
|
||||||
* @param {{ nettype: string, estimate_fee: number }} key
|
* @param {{ nettype: string, estimate_fee: number }} key
|
||||||
|
@ -47,11 +53,11 @@
|
||||||
}),
|
}),
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
|
});
|
||||||
onMount(() => {
|
|
||||||
loadCountries().then((data) => {
|
loadCountries().then((data) => {
|
||||||
countries = data;
|
countries = data;
|
||||||
});
|
});
|
||||||
|
|
||||||
handler.onChange((state) => loadData(state));
|
handler.onChange((state) => loadData(state));
|
||||||
handler.invalidate();
|
handler.invalidate();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
import { apiUri } from '$lib/utils/common';
|
import { apiUri } from '$lib/utils/common';
|
||||||
|
|
||||||
/** @param {import('@vincjo/datatables/remote/state')} state */
|
/**
|
||||||
|
* @typedef {import('@vincjo/datatables/remote').State} State
|
||||||
|
* @param {State} state - The state object from the data table.
|
||||||
|
*/
|
||||||
export const loadData = async (state) => {
|
export const loadData = async (state) => {
|
||||||
const response = await fetch(apiUri(`/api/v1/nodes?${getParams(state)}`));
|
const response = await fetch(apiUri(`/api/v1/nodes?${getParams(state)}`));
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
|
@ -14,6 +17,13 @@ export const loadCountries = async () => {
|
||||||
return json.data ?? [];
|
return json.data ?? [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const loadFees = async () => {
|
||||||
|
const response = await fetch(apiUri('/api/v1/fees'));
|
||||||
|
const json = await response.json();
|
||||||
|
return json.data ?? [];
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @param {State} state - The state object from the data table. */
|
||||||
const getParams = ({ pageNumber, rowsPerPage, sort, filters }) => {
|
const getParams = ({ pageNumber, rowsPerPage, sort, filters }) => {
|
||||||
let params = `page=${pageNumber}&limit=${rowsPerPage}`;
|
let params = `page=${pageNumber}&limit=${rowsPerPage}`;
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,9 @@ func (r *CronRepo) execCron(slug string) {
|
||||||
case "delete_old_probe_logs":
|
case "delete_old_probe_logs":
|
||||||
slog.Info(fmt.Sprintf("[CRON] Start running task: %s", slug))
|
slog.Info(fmt.Sprintf("[CRON] Start running task: %s", slug))
|
||||||
r.deleteOldProbeLogs()
|
r.deleteOldProbeLogs()
|
||||||
|
case "calculate_majority_fee":
|
||||||
|
slog.Info(fmt.Sprintf("[CRON] Start running task: %s", slug))
|
||||||
|
r.calculateMajorityFee()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,3 +162,48 @@ func (r *CronRepo) deleteOldProbeLogs() {
|
||||||
slog.Error(fmt.Sprintf("[CRON] Failed to delete old probe logs: %s", err))
|
slog.Error(fmt.Sprintf("[CRON] Failed to delete old probe logs: %s", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *CronRepo) calculateMajorityFee() {
|
||||||
|
netTypes := [3]string{"mainnet", "stagenet", "testnet"}
|
||||||
|
for _, net := range netTypes {
|
||||||
|
row, err := r.db.Query(`
|
||||||
|
SELECT
|
||||||
|
COUNT(id) AS node_count,
|
||||||
|
nettype,
|
||||||
|
estimate_fee
|
||||||
|
FROM
|
||||||
|
tbl_node
|
||||||
|
WHERE
|
||||||
|
nettype = ?
|
||||||
|
GROUP BY
|
||||||
|
estimate_fee
|
||||||
|
ORDER BY
|
||||||
|
node_count DESC
|
||||||
|
LIMIT 1`, net)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error(fmt.Sprintf("[CRON] Failed to calculate majority fee: %s", err))
|
||||||
|
}
|
||||||
|
defer row.Close()
|
||||||
|
|
||||||
|
var (
|
||||||
|
nettype string
|
||||||
|
estimateFee int
|
||||||
|
nodeCount int
|
||||||
|
)
|
||||||
|
|
||||||
|
for row.Next() {
|
||||||
|
err = row.Scan(&nodeCount, &nettype, &estimateFee)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error(fmt.Sprintf("[CRON] Failed to calculate majority fee: %s", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
query := `UPDATE tbl_fee SET estimate_fee = ?, node_count = ? WHERE nettype = ?`
|
||||||
|
_, err = r.db.Exec(query, estimateFee, nodeCount, nettype)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error(fmt.Sprintf("[CRON] Failed to update majority fee: %s", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
|
|
||||||
type migrateFn func(*DB) error
|
type migrateFn func(*DB) error
|
||||||
|
|
||||||
var dbMigrate = [...]migrateFn{v1}
|
var dbMigrate = [...]migrateFn{v1, v2}
|
||||||
|
|
||||||
func MigrateDb(db *DB) error {
|
func MigrateDb(db *DB) error {
|
||||||
version := getSchemaVersion(db)
|
version := getSchemaVersion(db)
|
||||||
|
@ -193,3 +193,61 @@ func v1(db *DB) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func v2(db *DB) error {
|
||||||
|
slog.Debug("[DB] Migrating database schema version 2")
|
||||||
|
|
||||||
|
// table: tbl_fee
|
||||||
|
slog.Debug("[DB] Creating table: tbl_fee")
|
||||||
|
_, err := db.Exec(`
|
||||||
|
CREATE TABLE tbl_fee (
|
||||||
|
nettype VARCHAR(100) NOT NULL DEFAULT '',
|
||||||
|
estimate_fee INT(9) UNSIGNED NOT NULL DEFAULT 0,
|
||||||
|
node_count INT(9) UNSIGNED NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (nettype)
|
||||||
|
)`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
slog.Debug("[DB] Adding default fee to table: tbl_fee")
|
||||||
|
_, err = db.Exec(`
|
||||||
|
INSERT INTO tbl_fee (
|
||||||
|
nettype,
|
||||||
|
estimate_fee,
|
||||||
|
node_count
|
||||||
|
) VALUES (
|
||||||
|
'mainnet',
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
), (
|
||||||
|
'stagenet',
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
), (
|
||||||
|
'testnet',
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Debug("[DB] Adding majority fee cron jobs to table: tbl_cron")
|
||||||
|
_, err = db.Exec(`
|
||||||
|
INSERT INTO tbl_cron (
|
||||||
|
title,
|
||||||
|
slug,
|
||||||
|
description,
|
||||||
|
run_every
|
||||||
|
) VALUES (
|
||||||
|
'Calculate majority fee',
|
||||||
|
'calculate_majority_fee',
|
||||||
|
'Calculate majority Monero fee',
|
||||||
|
300
|
||||||
|
);`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ type MoneroRepository interface {
|
||||||
Node(id int) (Node, error)
|
Node(id int) (Node, error)
|
||||||
Add(protocol string, host string, port uint) error
|
Add(protocol string, host string, port uint) error
|
||||||
Nodes(QueryNodes) (Nodes, error)
|
Nodes(QueryNodes) (Nodes, error)
|
||||||
NetFees() []NetFee
|
NetFees() []*NetFee
|
||||||
Countries() ([]Countries, error)
|
Countries() ([]Countries, error)
|
||||||
GiveJob(acceptTor int) (Node, error)
|
GiveJob(acceptTor int) (Node, error)
|
||||||
ProcessJob(report ProbeReport, proberId int64) error
|
ProcessJob(report ProbeReport, proberId int64) error
|
||||||
|
@ -80,13 +80,6 @@ func (r *MoneroRepo) Node(id int) (Node, error) {
|
||||||
return node, err
|
return node, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nodes represents a list of nodes
|
|
||||||
type Nodes struct {
|
|
||||||
TotalRows int `json:"total_rows"`
|
|
||||||
RowsPerPage int `json:"rows_per_page"`
|
|
||||||
Items []*Node `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// QueryNodes represents database query parameters
|
// QueryNodes represents database query parameters
|
||||||
type QueryNodes struct {
|
type QueryNodes struct {
|
||||||
Host string
|
Host string
|
||||||
|
@ -160,6 +153,13 @@ func (q QueryNodes) toSQL() (args []interface{}, where, sortBy, sortDirection st
|
||||||
return args, where, sortBy, sortDirection
|
return args, where, sortBy, sortDirection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nodes represents a list of nodes
|
||||||
|
type Nodes struct {
|
||||||
|
TotalRows int `json:"total_rows"`
|
||||||
|
RowsPerPage int `json:"rows_per_page"`
|
||||||
|
Items []*Node `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
// Get nodes from database
|
// Get nodes from database
|
||||||
func (r *MoneroRepo) Nodes(q QueryNodes) (Nodes, error) {
|
func (r *MoneroRepo) Nodes(q QueryNodes) (Nodes, error) {
|
||||||
args, where, sortBy, sortDirection := q.toSQL()
|
args, where, sortBy, sortDirection := q.toSQL()
|
||||||
|
@ -310,35 +310,20 @@ type NetFee struct {
|
||||||
NodeCount int `json:"node_count" db:"node_count"`
|
NodeCount int `json:"node_count" db:"node_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get majority net fee from database
|
// Get majority net fee from table tbl_fee
|
||||||
func (r *MoneroRepo) NetFees() []NetFee {
|
func (r *MoneroRepo) NetFees() []*NetFee {
|
||||||
// TODO: Create in-memory cache for this
|
var netFees []*NetFee
|
||||||
netTypes := [3]string{"mainnet", "stagenet", "testnet"}
|
err := r.db.Select(&netFees, `
|
||||||
netFees := []NetFee{}
|
|
||||||
|
|
||||||
for _, net := range netTypes {
|
|
||||||
fees := NetFee{}
|
|
||||||
err := r.db.Get(&fees, `
|
|
||||||
SELECT
|
SELECT
|
||||||
COUNT(id) AS node_count,
|
|
||||||
nettype,
|
nettype,
|
||||||
estimate_fee
|
estimate_fee,
|
||||||
|
node_count
|
||||||
FROM
|
FROM
|
||||||
tbl_node
|
tbl_fee
|
||||||
WHERE
|
`)
|
||||||
nettype = ?
|
|
||||||
GROUP BY
|
|
||||||
estimate_fee
|
|
||||||
ORDER BY
|
|
||||||
node_count DESC
|
|
||||||
LIMIT 1`, net)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("WARN:", err.Error())
|
slog.Error(fmt.Sprintf("[MONERO] Failed to get net fees: %s", err))
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
netFees = append(netFees, fees)
|
|
||||||
}
|
|
||||||
|
|
||||||
return netFees
|
return netFees
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue