Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f892d8275 | ||
|
|
8a673b6d7c | ||
|
|
fe0b9ecafb | ||
|
|
18b29033a8 | ||
|
|
3d9eb88ad4 | ||
|
|
4b4e18e564 | ||
|
|
2afb47a8c9 | ||
|
|
a1229102ee | ||
|
|
46e5cca125 | ||
|
|
9cc31a3cd3 | ||
|
|
ace7e61f45 | ||
|
|
a418dcd868 | ||
|
|
aa44f3d410 | ||
|
|
a2d3654c1b | ||
|
|
a7c4d6d26e | ||
|
|
a20c6350a2 | ||
|
|
0ea18d0d66 | ||
|
|
d130958050 | ||
|
|
de0b5eb1cd | ||
|
|
34375e1f8e | ||
|
|
6d8781de5e | ||
|
|
2d991e4b9d | ||
|
|
9e2006489e | ||
|
|
94ef015a94 | ||
|
|
7740d7dc97 | ||
|
|
f2497dec67 | ||
|
|
47a3a79041 | ||
|
|
cfddff320b | ||
|
|
f7ea01ca63 | ||
|
|
6fd667defb | ||
|
|
3036375362 | ||
|
|
bceb492ada | ||
|
|
26e9640d52 | ||
|
|
4848abda96 | ||
|
|
3a8e470caa | ||
|
|
77da8a5693 | ||
|
|
b955c0cc51 | ||
|
|
baa1d1bf36 | ||
|
|
7c917d82e7 | ||
|
|
c2ca8e4a80 | ||
|
|
3ecfef0ff4 | ||
|
|
3a8508d68d | ||
|
|
490fe51845 | ||
|
|
eadc0fadf1 | ||
|
|
8dbdf7a920 | ||
|
|
1da82420df | ||
|
|
aa5fc5e28f | ||
|
|
10e621ca92 | ||
|
|
69e821432e | ||
|
|
69870510d8 | ||
|
|
6353e7822a | ||
|
|
9b00f2a3cf | ||
|
|
9e6af5adfd | ||
|
|
44528b4518 | ||
|
|
138785c2e9 | ||
|
|
27893e227f | ||
|
|
bed02e0d4a | ||
|
|
45bddd4804 | ||
|
|
a620c635cb | ||
|
|
8828fd222c | ||
|
|
0d2becfe81 | ||
|
|
c9b601d698 | ||
|
|
7cec749db4 | ||
|
|
4c771dd537 | ||
|
|
a7bc8e551b | ||
|
|
b576bc5600 | ||
|
|
0b559ed4b4 | ||
|
|
34d185a8ec | ||
|
|
78882305b7 | ||
|
|
f51bb4dc13 | ||
|
|
c7a84fcabd | ||
|
|
c9ab1e8325 | ||
|
|
cc3887fd21 | ||
|
|
00b769814d | ||
|
|
509828b30e | ||
|
|
bea3bd8d20 | ||
|
|
d2b6e89c4d | ||
|
|
e3dc331508 | ||
|
|
6acd346ed8 | ||
|
|
494e2a13cc | ||
|
|
31fa601539 | ||
|
|
952a5cb196 | ||
|
|
6fbedae7b6 | ||
|
|
f999ebff18 | ||
|
|
7abfb5151b | ||
|
|
f49728c461 | ||
|
|
621e13b17f |
27
README.md
27
README.md
@ -1,3 +1,8 @@
|
||||
## This repo is looking for maintainers! Please reach out if interested.
|
||||
|
||||
--------
|
||||
|
||||
|
||||
# NOMP 
|
||||
#### Node Open Mining Portal
|
||||
|
||||
@ -8,6 +13,12 @@ responsive user-friendly front-end website featuring mining instructions, in-dep
|
||||
#### Production Usage Notice
|
||||
This is beta software. All of the following are things that can change and break an existing NOMP setup: functionality of any feature, structure of configuration files and structure of redis data. If you use this software in production then *DO NOT* pull new code straight into production usage because it can and often will break your setup and require you to tweak things like config files or redis data.
|
||||
|
||||
#### Paid Solution
|
||||
Usage of this software requires abilities with sysadmin, database admin, coin daemons, and sometimes a bit of programming. Running a production pool can literally be more work than a full-time job.
|
||||
|
||||
|
||||
**Coin switching & auto-exchanging for payouts in BTC/LTC** to miners is a feature that very likely will not be included in this project.
|
||||
|
||||
|
||||
#### Table of Contents
|
||||
* [Features](#features)
|
||||
@ -104,21 +115,13 @@ didn't follow the instructions in this README. Please __read the usage instructi
|
||||
If your pool uses NOMP let us know and we will list your website here.
|
||||
|
||||
##### Some pools using NOMP or node-stratum-module:
|
||||
* http://chunkypools.com
|
||||
* http://clevermining.com
|
||||
* http://rapidhash.net
|
||||
* http://suchpool.pw
|
||||
* http://hashfaster.com
|
||||
* http://miningpoolhub.com
|
||||
* http://teamdoge.com
|
||||
* http://miningwith.us
|
||||
* http://kryptochaos.com
|
||||
* http://uberpools.org
|
||||
* http://onebtcplace.com
|
||||
* http://minr.es
|
||||
* http://mining.theminingpools.com
|
||||
* http://www.omargpools.ca/pools.html
|
||||
* http://pool.trademybit.com/
|
||||
* http://miningpools.tk
|
||||
* http://umine.co.uk
|
||||
|
||||
Usage
|
||||
=====
|
||||
@ -348,7 +351,7 @@ Here is an example of the required fields:
|
||||
source code as the pchMessageStart variable.
|
||||
For example, litecoin mainnet magic: http://git.io/Bi8YFw
|
||||
And for litecoin testnet magic: http://git.io/NXBYJA */
|
||||
"peerMagic": "fbc0b6db" //optional
|
||||
"peerMagic": "fbc0b6db", //optional
|
||||
"peerMagicTestnet": "fcc1b7dc" //optional
|
||||
|
||||
//"txMessages": false, //options - defaults to false
|
||||
@ -524,7 +527,7 @@ output from NOMP.
|
||||
|
||||
#### Upgrading NOMP
|
||||
When updating NOMP to the latest code its important to not only `git pull` the latest from this repo, but to also update
|
||||
the `node-statum-pool` and `node-multi-hashing` modules, and any config files that may have been changed.
|
||||
the `node-stratum-pool` and `node-multi-hashing` modules, and any config files that may have been changed.
|
||||
* Inside your NOMP directory (where the init.js script is) do `git pull` to get the latest NOMP code.
|
||||
* Remove the dependenices by deleting the `node_modules` directory with `rm -r node_modules`.
|
||||
* Run `npm update` to force updating/reinstalling of the dependencies.
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
{
|
||||
"name": "21coin",
|
||||
"symbol": "21",
|
||||
"algorithm": "sha256"
|
||||
"algorithm": "sha256", │·····································
|
||||
"peerMagic": "21212121", │·····································
|
||||
"peerMagicTestnet": "01fefe05"
|
||||
}
|
||||
|
||||
5
coins/42.json
Normal file
5
coins/42.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "42",
|
||||
"symbol": "42",
|
||||
"algorithm": "scrypt"
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
{
|
||||
"name": "Alphacoin",
|
||||
"symbol": "ALF",
|
||||
"algorithm": "scrypt"
|
||||
}
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "fbc0b6db",
|
||||
"peerMagicTestnet": "fcc1b7dc"
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
{
|
||||
"name": "Benjamins",
|
||||
"symbol": "BEN",
|
||||
"algorithm": "sha256"
|
||||
"algorithm": "sha256",
|
||||
"peerMagic": "de698778", │·····································
|
||||
"peerMagicTestnet": "0b110907"
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
{
|
||||
"name": "Bottlecaps",
|
||||
"symbol": "CAP",
|
||||
"algorithm": "scrypt"
|
||||
}
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "e4e8e9e5",
|
||||
"peerMagicTestnet": "cdf2c0ef"
|
||||
}
|
||||
|
||||
7
coins/bunnycoin.json
Normal file
7
coins/bunnycoin.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "BunnyCoin",
|
||||
"symbol": "BUN",
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "c0c0c0c0",
|
||||
"peerMagicTestnet": "fcc1b7dc"
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
{
|
||||
"name": "Casinocoin",
|
||||
"symbol": "CSC",
|
||||
"algorithm": "scrypt"
|
||||
}
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "fac3b6da",
|
||||
"peerMagicTestnet": "fcc1b7dc"
|
||||
}
|
||||
|
||||
7
coins/coino.json
Normal file
7
coins/coino.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Coino",
|
||||
"symbol": "COINO",
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "f1d1a7d8",
|
||||
"peerMagicTestnet": "fcc1b7dc"
|
||||
}
|
||||
7
coins/cryptogenicbullion.json
Normal file
7
coins/cryptogenicbullion.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "CryptogenicBullion",
|
||||
"symbol": "CGB",
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "e4e8e9e5",
|
||||
"peerMagicTestnet": "cdf2c0ef"
|
||||
}
|
||||
7
coins/cryptographicanomaly.json
Normal file
7
coins/cryptographicanomaly.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "CryptographicAnomaly",
|
||||
"symbol": "CGA",
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "fbc0b6db",
|
||||
"peerMagicTestnet": "fcc1b7dc"
|
||||
}
|
||||
@ -1,5 +1,7 @@
|
||||
{
|
||||
"name": "Digibyte",
|
||||
"symbol": "DGB",
|
||||
"algorithm": "scrypt"
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "fac3b6da",
|
||||
"peerMagicTestnet": "fdc8bddd"
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "eMark",
|
||||
"symbol": "DEM",
|
||||
"algorithm": "sha256"
|
||||
"algorithm": "sha256",
|
||||
"reward": "POS"
|
||||
}
|
||||
|
||||
@ -2,5 +2,5 @@
|
||||
"name": "Fastcoin",
|
||||
"symbol": "FST",
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "fbc0b6db"
|
||||
}
|
||||
"peerMagic": "fdc2b5dc"
|
||||
}
|
||||
|
||||
5
coins/fluttercoin.json
Normal file
5
coins/fluttercoin.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "FlutterCoin",
|
||||
"symbol": "FLT",
|
||||
"algorithm": "scrypt"
|
||||
}
|
||||
7
coins/globaltoken.json
Normal file
7
coins/globaltoken.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "GlobalToken",
|
||||
"symbol": "GLT",
|
||||
"algorithm": "sha256",
|
||||
"peerMagic": "c708d32d",
|
||||
"peerMagicTestnet": "3a6f375b"
|
||||
}
|
||||
7
coins/infinitecoin.conf
Normal file
7
coins/infinitecoin.conf
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Infinitecoin",
|
||||
"symbol": "IFC",
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "fbc0b6db",
|
||||
"peerMagicTestnet": "fcc1b7dc"
|
||||
}
|
||||
6
coins/kumacoin.json
Normal file
6
coins/kumacoin.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Kumacoin",
|
||||
"symbol": "KUMA",
|
||||
"algorithm": "quark",
|
||||
"mposDiffMultiplier": 256
|
||||
}
|
||||
@ -3,5 +3,5 @@
|
||||
"symbol": "LTC",
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "fbc0b6db",
|
||||
"peerMagicTestnet": "fcc1b7dc"
|
||||
}
|
||||
"peerMagicTestnet": "fdd2c8f1"
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
{
|
||||
"name": "Mazacoin",
|
||||
"symbol": "MZC",
|
||||
"algorithm": "sha256"
|
||||
"algorithm": "sha256",
|
||||
"peerMagic": "f8b503df", │·····································
|
||||
"peerMagicTestnet": "05fea901"
|
||||
}
|
||||
|
||||
5
coins/monacoin.json
Normal file
5
coins/monacoin.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Monacoin",
|
||||
"symbol": "MONA",
|
||||
"algorithm": "scrypt"
|
||||
}
|
||||
5
coins/sayacoin.json
Normal file
5
coins/sayacoin.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Sayacoin",
|
||||
"symbol": "SYC",
|
||||
"algorithm": "sha256"
|
||||
}
|
||||
5
coins/sha1coin.json
Normal file
5
coins/sha1coin.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Sha1coin",
|
||||
"symbol": "SHA",
|
||||
"algorithm": "sha1coin"
|
||||
}
|
||||
7
coins/viacoin.json
Normal file
7
coins/viacoin.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Viacoin",
|
||||
"symbol": "VIA",
|
||||
"algorithm": "scrypt",
|
||||
"peerMagic": "0f68c6cb",
|
||||
"peerMagicTestnet": "a9c5ef92"
|
||||
}
|
||||
@ -40,7 +40,7 @@
|
||||
"hashrateWindow": 300
|
||||
},
|
||||
"adminCenter": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"password": "password"
|
||||
}
|
||||
},
|
||||
|
||||
@ -15,9 +15,11 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||
this.handleApiRequest = function(req, res, next){
|
||||
switch(req.params.method){
|
||||
case 'stats':
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(portalStats.statsString);
|
||||
return;
|
||||
case 'pool_stats':
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(portalStats.statPoolHistory));
|
||||
return;
|
||||
case 'live_stats':
|
||||
@ -51,4 +53,4 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
@ -70,10 +70,21 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||
callback(true);
|
||||
}
|
||||
else if (!result.response || !result.response.ismine) {
|
||||
logger.error(logSystem, logComponent,
|
||||
'Daemon does not own pool address - payment processing can not be done with this daemon, '
|
||||
+ JSON.stringify(result.response));
|
||||
callback(true);
|
||||
daemon.cmd('getaddressinfo', [poolOptions.address], function(result) {
|
||||
if (result.error){
|
||||
logger.error(logSystem, logComponent, 'Error with payment processing daemon, getaddressinfo failed ... ' + JSON.stringify(result.error));
|
||||
callback(true);
|
||||
}
|
||||
else if (!result.response || !result.response.ismine) {
|
||||
logger.error(logSystem, logComponent,
|
||||
'Daemon does not own pool address - payment processing can not be done with this daemon, '
|
||||
+ JSON.stringify(result.response));
|
||||
callback(true);
|
||||
}
|
||||
else{
|
||||
callback()
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
else{
|
||||
callback()
|
||||
@ -169,7 +180,7 @@ function SetupForPool(logger, poolOptions, setupFinished){
|
||||
|
||||
var workers = {};
|
||||
for (var w in results[0]){
|
||||
workers[w] = {balance: coinsToSatoshies(parseInt(results[0][w]))};
|
||||
workers[w] = {balance: coinsToSatoshies(parseFloat(results[0][w]))};
|
||||
}
|
||||
|
||||
var rounds = results[1].map(function(r){
|
||||
|
||||
@ -184,12 +184,16 @@ module.exports = function(logger){
|
||||
logger.debug(logSystem, logComponent, logSubCat, 'We thought a block was found but it was rejected by the daemon, share data: ' + shareData);
|
||||
|
||||
else if (isValidBlock)
|
||||
logger.debug(logSystem, logComponent, logSubCat, 'Block found: ' + data.blockHash);
|
||||
logger.debug(logSystem, logComponent, logSubCat, 'Block found: ' + data.blockHash + ' by ' + data.worker);
|
||||
|
||||
if (isValidShare)
|
||||
if (isValidShare) {
|
||||
if(data.shareDiff > 1000000000)
|
||||
logger.debug(logSystem, logComponent, logSubCat, 'Share was found with diff higher than 1.000.000.000!');
|
||||
else if(data.shareDiff > 1000000)
|
||||
logger.debug(logSystem, logComponent, logSubCat, 'Share was found with diff higher than 1.000.000!');
|
||||
logger.debug(logSystem, logComponent, logSubCat, 'Share accepted at diff ' + data.difficulty + '/' + data.shareDiff + ' by ' + data.worker + ' [' + data.ip + ']' );
|
||||
|
||||
else if (!isValidShare)
|
||||
} else if (!isValidShare)
|
||||
logger.debug(logSystem, logComponent, logSubCat, 'Share rejected: ' + shareData);
|
||||
|
||||
handlers.share(isValidShare, isValidBlock, data)
|
||||
@ -273,7 +277,7 @@ module.exports = function(logger){
|
||||
if (pools[currentPool])
|
||||
pools[currentPool].getStratumServer().handleNewClient(socket);
|
||||
else
|
||||
pools[initialPool].getStratumServer().handleNewClient(socket);
|
||||
pools[initalPool].getStratumServer().handleNewClient(socket);
|
||||
|
||||
}).listen(parseInt(port), function() {
|
||||
logger.debug(logSystem, logComponent, logSubCat, 'Switching "' + switchName
|
||||
|
||||
@ -36,7 +36,7 @@ module.exports = function(logger, poolConfig){
|
||||
logger.error(logSystem, logComponent, logSubCat, 'Redis client had an error: ' + JSON.stringify(err))
|
||||
});
|
||||
connection.on('end', function(){
|
||||
logger.error(logSystem, logComponent, logSubCat, 'Connection to redis database as been ended');
|
||||
logger.error(logSystem, logComponent, logSubCat, 'Connection to redis database has been ended');
|
||||
});
|
||||
|
||||
connection.info(function(error, response){
|
||||
|
||||
@ -114,7 +114,7 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||
['hgetall', ':stats'],
|
||||
['scard', ':blocksPending'],
|
||||
['scard', ':blocksConfirmed'],
|
||||
['scard', ':blocksOrphaned']
|
||||
['scard', ':blocksKicked']
|
||||
];
|
||||
|
||||
var commandsPerCoin = redisCommandTemplates.length;
|
||||
@ -274,9 +274,9 @@ module.exports = function(logger, portalConfig, poolConfigs){
|
||||
var i = -1;
|
||||
var byteUnits = [ ' KH', ' MH', ' GH', ' TH', ' PH' ];
|
||||
do {
|
||||
hashrate = hashrate / 1024;
|
||||
hashrate = hashrate / 1000;
|
||||
i++;
|
||||
} while (hashrate > 1024);
|
||||
} while (hashrate > 1000);
|
||||
return hashrate.toFixed(2) + byteUnits[i];
|
||||
};
|
||||
|
||||
|
||||
20
package.json
20
package.json
@ -12,9 +12,9 @@
|
||||
"litecoin",
|
||||
"scrypt"
|
||||
],
|
||||
"homepage": "https://github.com/zone117x/node-open-mining-portal",
|
||||
"homepage": "https://github.com/ranchimall/node-open-mining-portal",
|
||||
"bugs": {
|
||||
"url": "https://github.com/zone117x/node-open-mining-portal/issues"
|
||||
"url": "https://github.com/ranchimall/node-open-mining-portal/issues"
|
||||
},
|
||||
"license": "GPL-2.0",
|
||||
"author": "Matthew Little",
|
||||
@ -28,24 +28,24 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zone117x/node-open-mining-portal.git"
|
||||
"url": "https://github.com/ranchimall/node-open-mining-portal.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"stratum-pool": "git://github.com/zone117x/node-stratum-pool.git",
|
||||
"dateformat": "*",
|
||||
"stratum-pool": "git://github.com/ranchimall/node-stratum-pool.git",
|
||||
"dateformat": "1.0.12",
|
||||
"node-json-minify": "*",
|
||||
"redis": "*",
|
||||
"redis": "0.12.1",
|
||||
"mysql": "*",
|
||||
"async": "*",
|
||||
"async": "1.5.2",
|
||||
"express": "*",
|
||||
"body-parser": "*",
|
||||
"compression": "*",
|
||||
"dot": "*",
|
||||
"colors": "*",
|
||||
"node-watch": "*",
|
||||
"request": "*",
|
||||
"node-watch": "0.5.9",
|
||||
"request": "2.69.0",
|
||||
"nonce": "*",
|
||||
"bignum": "*",
|
||||
"bignum": "0.12.1",
|
||||
"extend": "*"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@ -185,9 +185,9 @@
|
||||
<div id="menu">
|
||||
|
||||
{{? (function(){
|
||||
if (!it.portalConfig.proxy) return false;
|
||||
for (var p in it.portalConfig.proxy){
|
||||
if (it.portalConfig.proxy[p].enabled)
|
||||
if (!it.portalConfig.switching) return false;
|
||||
for (var p in it.portalConfig.switching){
|
||||
if (it.portalConfig.switching[p].enabled)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -197,14 +197,14 @@
|
||||
{{?}}
|
||||
|
||||
<div class="menuList">
|
||||
{{ for (var p in it.portalConfig.proxy){
|
||||
if (!it.portalConfig.proxy[p].enabled) continue;
|
||||
{{ for (var p in it.portalConfig.switching){
|
||||
if (!it.portalConfig.switching[p].enabled) continue;
|
||||
var info = {
|
||||
algo: p,
|
||||
ports: {},
|
||||
host: it.portalConfig.website.stratumHost
|
||||
};
|
||||
info.ports[it.portalConfig.proxy[p].port] = {diff: it.portalConfig.proxy[p].diff};
|
||||
info.ports[it.portalConfig.switching[p].port] = {diff: it.portalConfig.switching[p].diff};
|
||||
info = JSON.stringify(info).replace(/"/g, '"');
|
||||
}}
|
||||
<a href="#" class="poolOption" data-info="{{=info}}">{{=p}}</a>
|
||||
@ -315,4 +315,4 @@
|
||||
alert('NOMP App development still in progress...');
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user