Skip to content
This repository was archived by the owner on Oct 20, 2023. It is now read-only.

Commit 4172624

Browse files
author
Oliver Weichhold
authored
Dev (#171)
* Log and record miner submitting block solution * Fix top miner query * Don't expose disabled pools via API * External socket binding * API Fix * Monero: address validation fixes * Monero: More Monero integrated address fixes * Monero: Better handling for invalid payment Ids * Monero: Payout-batch address fix * Monero: Actually detect network type in payout handler
1 parent 2586764 commit 4172624

File tree

17 files changed

+282
-73
lines changed

17 files changed

+282
-73
lines changed

src/MiningCore/Api/ApiServer.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ private PoolConfig GetPool(HttpContext context, Match m)
120120

121121
if (!string.IsNullOrEmpty(poolId))
122122
{
123-
var pool = clusterConfig.Pools.FirstOrDefault(x => x.Id == poolId);
123+
var pool = clusterConfig.Pools.FirstOrDefault(x => x.Id == poolId && x.Enabled);
124124

125125
if (pool != null)
126126
return pool;
@@ -218,7 +218,7 @@ private async Task GetPoolInfosAsync(HttpContext context, Match m)
218218
{
219219
var response = new GetPoolsResponse
220220
{
221-
Pools = clusterConfig.Pools.Select(config =>
221+
Pools = clusterConfig.Pools.Where(x=> x.Enabled).Select(config =>
222222
{
223223
// load stats
224224
var stats = cf.Run(con => statsRepo.GetLastPoolStats(con, config.Id));

src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ public virtual async Task<BitcoinShare> SubmitShareAsync(StratumClient worker, o
346346

347347
if (share.IsBlockCandidate)
348348
{
349-
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlockHash}]");
349+
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlockHash}] submitted by {minerName}");
350350

351351
// persist the coinbase transaction-hash to allow the payment processor
352352
// to verify later on that the pool has received the reward for the block

src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ public async Task<EthereumShare> SubmitShareAsync(StratumClient worker,
404404
Contract.RequiresNonNull(request, nameof(request));
405405

406406
logger.LogInvoke(LogCat, new[] { worker.ConnectionId });
407+
var context = worker.GetContextAs<EthereumWorkerContext>();
407408

408409
// var miner = request[0];
409410
var jobId = request[1];
@@ -429,7 +430,7 @@ public async Task<EthereumShare> SubmitShareAsync(StratumClient worker,
429430

430431
if (share.IsBlockCandidate)
431432
{
432-
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight}");
433+
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} submitted by {context.MinerName}");
433434
}
434435
}
435436

src/MiningCore/Blockchain/Monero/MoneroConstants.cs

+31-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ portions of the Software.
1818
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1919
*/
2020

21+
using System;
2122
using System.Collections.Generic;
2223
using System.Globalization;
2324
using System.Text.RegularExpressions;
@@ -38,13 +39,41 @@ public class MoneroConstants
3839
{
3940
public const string WalletDaemonCategory = "wallet";
4041

41-
public static readonly Dictionary<CoinType, int> AddressLength = new Dictionary<CoinType, int>
42+
public static readonly Dictionary<CoinType, UInt64> AddressLength = new Dictionary<CoinType, UInt64>
4243
{
4344
{ CoinType.XMR, 95 },
4445
{ CoinType.ETN, 98 },
4546
{ CoinType.AEON, 97 },
4647
};
4748

49+
public static readonly Dictionary<CoinType, UInt64> AddressPrefix = new Dictionary<CoinType, UInt64>
50+
{
51+
{ CoinType.XMR, 18 },
52+
{ CoinType.ETN, 18018 },
53+
{ CoinType.AEON, 178 },
54+
};
55+
56+
public static readonly Dictionary<CoinType, UInt64> AddressPrefixTestnet = new Dictionary<CoinType, UInt64>
57+
{
58+
{ CoinType.XMR, 53 },
59+
{ CoinType.ETN, 53 },
60+
{ CoinType.AEON, 178 },
61+
};
62+
63+
public static readonly Dictionary<CoinType, UInt64> AddressPrefixIntegrated = new Dictionary<CoinType, UInt64>
64+
{
65+
{ CoinType.XMR, 19 },
66+
{ CoinType.ETN, 18019 },
67+
{ CoinType.AEON, 178 },
68+
};
69+
70+
public static readonly Dictionary<CoinType, UInt64> AddressPrefixIntegratedTestnet = new Dictionary<CoinType, UInt64>
71+
{
72+
{ CoinType.XMR, 54 },
73+
{ CoinType.ETN, 54 },
74+
{ CoinType.AEON, 178 },
75+
};
76+
4877
public static readonly Dictionary<CoinType, decimal> SmallestUnit = new Dictionary<CoinType, decimal>
4978
{
5079
{ CoinType.XMR, Piconero },
@@ -57,6 +86,7 @@ public class MoneroConstants
5786
public const int MoneroRpcMethodNotFound = -32601;
5887
public const char MainNetAddressPrefix = '4';
5988
public const char TestNetAddressPrefix = '9';
89+
public const int PaymentIdHexLength = 64;
6090
public static readonly Regex RegexValidNonce = new Regex("^[0-9a-f]{8}$", RegexOptions.Compiled);
6191

6292
public static readonly BigInteger Diff1 = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16);

src/MiningCore/Blockchain/Monero/MoneroJobManager.cs

+38-12
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2626
using System.Security.Cryptography;
2727
using System.Threading.Tasks;
2828
using Autofac;
29+
using Microsoft.Extensions.DependencyInjection;
2930
using MiningCore.Blockchain.Bitcoin;
3031
using MiningCore.Blockchain.Monero.DaemonRequests;
3132
using MiningCore.Blockchain.Monero.DaemonResponses;
@@ -75,7 +76,7 @@ public MoneroJobManager(
7576
private readonly NotificationService notificationService;
7677
private readonly IMasterClock clock;
7778
private MoneroNetworkType networkType;
78-
private uint poolAddressBase58Prefix;
79+
private UInt64 poolAddressBase58Prefix;
7980
private DaemonEndpointConfig[] walletDaemonEndpoints;
8081

8182
protected async Task<bool> UpdateJob()
@@ -198,10 +199,6 @@ public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfi
198199
this.poolConfig = poolConfig;
199200
this.clusterConfig = clusterConfig;
200201

201-
poolAddressBase58Prefix = LibCryptonote.DecodeAddress(poolConfig.Address);
202-
if (poolAddressBase58Prefix == 0)
203-
logger.ThrowLogPoolStartupException("Unable to decode pool-address", LogCat);
204-
205202
// extract standard daemon endpoints
206203
daemonEndpoints = poolConfig.Daemons
207204
.Where(x => string.IsNullOrEmpty(x.Category))
@@ -222,12 +219,23 @@ public bool ValidateAddress(string address)
222219
{
223220
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty");
224221

225-
if (address.Length != MoneroConstants.AddressLength[poolConfig.Coin.Type])
226-
return false;
227-
228222
var addressPrefix = LibCryptonote.DecodeAddress(address);
229-
if (addressPrefix != poolAddressBase58Prefix)
230-
return false;
223+
var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address);
224+
225+
switch (networkType)
226+
{
227+
case MoneroNetworkType.Main:
228+
if (addressPrefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type] &&
229+
addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type])
230+
return false;
231+
break;
232+
233+
case MoneroNetworkType.Test:
234+
if (addressPrefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type] &&
235+
addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type])
236+
return false;
237+
break;
238+
}
231239

232240
return true;
233241
}
@@ -255,9 +263,9 @@ public async Task<MoneroShare> SubmitShareAsync(StratumClient worker,
255263
{
256264
Contract.RequiresNonNull(worker, nameof(worker));
257265
Contract.RequiresNonNull(request, nameof(request));
258-
var context = worker.GetContextAs<MoneroWorkerContext>();
259266

260267
logger.LogInvoke(LogCat, new[] { worker.ConnectionId });
268+
var context = worker.GetContextAs<MoneroWorkerContext>();
261269

262270
var job = currentJob;
263271
if (workerJob.Height != job?.BlockTemplate.Height)
@@ -275,7 +283,7 @@ public async Task<MoneroShare> SubmitShareAsync(StratumClient worker,
275283

276284
if (share.IsBlockCandidate)
277285
{
278-
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlobHash.Substring(0, 6)}]");
286+
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlobHash.Substring(0, 6)}] submitted by {context.MinerName}");
279287

280288
share.TransactionConfirmationData = share.BlobHash;
281289
}
@@ -403,6 +411,24 @@ protected override async Task PostStartInitAsync()
403411
// chain detection
404412
networkType = info.IsTestnet ? MoneroNetworkType.Test : MoneroNetworkType.Main;
405413

414+
// address validation
415+
poolAddressBase58Prefix = LibCryptonote.DecodeAddress(poolConfig.Address);
416+
if (poolAddressBase58Prefix == 0)
417+
logger.ThrowLogPoolStartupException("Unable to decode pool-address", LogCat);
418+
419+
switch(networkType)
420+
{
421+
case MoneroNetworkType.Main:
422+
if(poolAddressBase58Prefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type])
423+
logger.ThrowLogPoolStartupException($"Invalid pool address prefix. Expected {MoneroConstants.AddressPrefix[poolConfig.Coin.Type]}, got {poolAddressBase58Prefix}", LogCat);
424+
break;
425+
426+
case MoneroNetworkType.Test:
427+
if (poolAddressBase58Prefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type])
428+
logger.ThrowLogPoolStartupException($"Invalid pool address prefix. Expected {MoneroConstants.AddressPrefix[poolConfig.Coin.Type]}, got {poolAddressBase58Prefix}", LogCat);
429+
break;
430+
}
431+
406432
ConfigureRewards();
407433

408434
// update stats

src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs

+99-15
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3131
using MiningCore.Configuration;
3232
using MiningCore.DaemonInterface;
3333
using MiningCore.Extensions;
34+
using MiningCore.Native;
3435
using MiningCore.Notifications;
3536
using MiningCore.Payments;
3637
using MiningCore.Persistence;
@@ -139,10 +140,15 @@ private async Task PayoutBatch(Balance[] balances)
139140
{
140141
Destinations = balances
141142
.Where(x => x.Amount > 0)
142-
.Select(x => new TransferDestination
143+
.Select(x =>
143144
{
144-
Address = x.Address,
145-
Amount = (ulong) Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type])
145+
ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId);
146+
147+
return new TransferDestination
148+
{
149+
Address = address,
150+
Amount = (ulong) Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type])
151+
};
146152
}).ToArray(),
147153

148154
GetTxKey = true
@@ -190,10 +196,15 @@ private async Task PayoutBatch(Balance[] balances)
190196
// update request
191197
request.Destinations = page
192198
.Where(x => x.Amount > 0)
193-
.Select(x => new TransferDestination
199+
.Select(x =>
194200
{
195-
Address = x.Address,
196-
Amount = (ulong)Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type])
201+
ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId);
202+
203+
return new TransferDestination
204+
{
205+
Address = address,
206+
Amount = (ulong) Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type])
207+
};
197208
}).ToArray();
198209

199210
logger.Info(() => $"[{LogCategory}] Page {i + 1}: Paying out {FormatAmount(page.Sum(x => x.Amount))} to {page.Length} addresses");
@@ -210,19 +221,33 @@ private async Task PayoutBatch(Balance[] balances)
210221
HandleTransferResponse(transferResponse, balances);
211222
}
212223

213-
private async Task PayoutToPaymentId(Balance balance)
224+
private void ExtractAddressAndPaymentId(string input, out string address, out string paymentId)
214225
{
215-
// extract paymentId
216-
var address = (string) null;
217-
var paymentId = (string) null;
226+
paymentId = null;
227+
var index = input.IndexOf(PayoutConstants.PayoutInfoSeperator);
218228

219-
var index = balance.Address.IndexOf(PayoutConstants.PayoutInfoSeperator);
220229
if (index != -1)
221230
{
222-
paymentId = balance.Address.Substring(index + 1);
223-
address = balance.Address.Substring(0, index);
231+
address = input.Substring(0, index);
232+
233+
if (index + 1 < input.Length)
234+
{
235+
paymentId = input.Substring(index + 1);
236+
237+
// ignore invalid payment ids
238+
if (paymentId.Length != MoneroConstants.PaymentIdHexLength)
239+
paymentId = null;
240+
}
224241
}
225242

243+
else
244+
address = input;
245+
}
246+
247+
private async Task PayoutToPaymentId(Balance balance)
248+
{
249+
ExtractAddressAndPaymentId(balance.Address, out var address, out var paymentId);
250+
226251
if (string.IsNullOrEmpty(paymentId))
227252
throw new InvalidOperationException("invalid paymentid");
228253

@@ -290,6 +315,9 @@ public async Task ConfigureAsync(ClusterConfig clusterConfig, PoolConfig poolCon
290315
walletDaemon = new DaemonClient(jsonSerializerSettings);
291316
walletDaemon.Configure(walletDaemonEndpoints, MoneroConstants.DaemonRpcLocation);
292317

318+
// detect network
319+
await GetNetworkTypeAsync();
320+
293321
// detect transfer_split support
294322
var response = await walletDaemon.ExecuteCmdSingleAsync<TransferResponse>(MWC.TransferSplit);
295323
walletSupportsTransferSplit = response.Error.Code != MoneroConstants.MoneroRpcMethodNotFound;
@@ -413,9 +441,65 @@ public async Task PayoutAsync(Balance[] balances)
413441
#endif
414442
}
415443

444+
// validate addresses
445+
balances = balances
446+
.Where(x =>
447+
{
448+
ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId);
449+
450+
var addressPrefix = LibCryptonote.DecodeAddress(address);
451+
var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address);
452+
453+
switch (networkType)
454+
{
455+
case MoneroNetworkType.Main:
456+
if (addressPrefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type] &&
457+
addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type])
458+
{
459+
logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address {x.Address}");
460+
return false;
461+
}
462+
break;
463+
464+
case MoneroNetworkType.Test:
465+
if (addressPrefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type] &&
466+
addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type])
467+
{
468+
logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address {x.Address}");
469+
return false;
470+
}
471+
break;
472+
}
473+
474+
return true;
475+
})
476+
.ToArray();
477+
416478
// simple balances first
417479
var simpleBalances = balances
418-
.Where(x => !x.Address.Contains(PayoutConstants.PayoutInfoSeperator))
480+
.Where(x =>
481+
{
482+
ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId);
483+
484+
var hasPaymentId = paymentId != null;
485+
var isIntegratedAddress = false;
486+
var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address);
487+
488+
switch (networkType)
489+
{
490+
case MoneroNetworkType.Main:
491+
if (addressIntegratedPrefix == MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type])
492+
isIntegratedAddress = true;
493+
break;
494+
495+
case MoneroNetworkType.Test:
496+
if (addressIntegratedPrefix == MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type])
497+
isIntegratedAddress = true;
498+
break;
499+
}
500+
501+
return !hasPaymentId && !isIntegratedAddress;
502+
})
419503
.ToArray();
420504

421505
if (simpleBalances.Length > 0)
@@ -425,7 +509,7 @@ public async Task PayoutAsync(Balance[] balances)
425509
var minimumPaymentToPaymentId = extraConfig?.MinimumPaymentToPaymentId ?? poolConfig.PaymentProcessing.MinimumPayment;
426510

427511
var paymentIdBalances = balances.Except(simpleBalances)
428-
.Where(x => x.Address.Contains(PayoutConstants.PayoutInfoSeperator) && x.Amount >= minimumPaymentToPaymentId)
512+
.Where(x => x.Amount >= minimumPaymentToPaymentId)
429513
.ToArray();
430514

431515
foreach(var balance in paymentIdBalances)

src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ public override async Task<BitcoinShare> SubmitShareAsync(StratumClient worker,
163163

164164
if (share.IsBlockCandidate)
165165
{
166-
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlockHash}]");
166+
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} [{share.BlockHash}] submitted by {minerName}");
167167

168168
// persist the coinbase transaction-hash to allow the payment processor
169169
// to verify later on that the pool has received the reward for the block

0 commit comments

Comments
 (0)