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

Commit

Permalink
Dev (#171)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Oliver Weichhold authored Jan 12, 2018
1 parent 2586764 commit 4172624
Show file tree
Hide file tree
Showing 17 changed files with 282 additions and 73 deletions.
4 changes: 2 additions & 2 deletions src/MiningCore/Api/ApiServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ private PoolConfig GetPool(HttpContext context, Match m)

if (!string.IsNullOrEmpty(poolId))
{
var pool = clusterConfig.Pools.FirstOrDefault(x => x.Id == poolId);
var pool = clusterConfig.Pools.FirstOrDefault(x => x.Id == poolId && x.Enabled);

if (pool != null)
return pool;
Expand Down Expand Up @@ -218,7 +218,7 @@ private async Task GetPoolInfosAsync(HttpContext context, Match m)
{
var response = new GetPoolsResponse
{
Pools = clusterConfig.Pools.Select(config =>
Pools = clusterConfig.Pools.Where(x=> x.Enabled).Select(config =>
{
// load stats
var stats = cf.Run(con => statsRepo.GetLastPoolStats(con, config.Id));
Expand Down
2 changes: 1 addition & 1 deletion src/MiningCore/Blockchain/Bitcoin/BitcoinJobManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ public virtual async Task<BitcoinShare> SubmitShareAsync(StratumClient worker, o

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

// persist the coinbase transaction-hash to allow the payment processor
// to verify later on that the pool has received the reward for the block
Expand Down
3 changes: 2 additions & 1 deletion src/MiningCore/Blockchain/Ethereum/EthereumJobManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ public async Task<EthereumShare> SubmitShareAsync(StratumClient worker,
Contract.RequiresNonNull(request, nameof(request));

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

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

if (share.IsBlockCandidate)
{
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight}");
logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} submitted by {context.MinerName}");
}
}

Expand Down
32 changes: 31 additions & 1 deletion src/MiningCore/Blockchain/Monero/MoneroConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ portions of the Software.
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
Expand All @@ -38,13 +39,41 @@ public class MoneroConstants
{
public const string WalletDaemonCategory = "wallet";

public static readonly Dictionary<CoinType, int> AddressLength = new Dictionary<CoinType, int>
public static readonly Dictionary<CoinType, UInt64> AddressLength = new Dictionary<CoinType, UInt64>
{
{ CoinType.XMR, 95 },
{ CoinType.ETN, 98 },
{ CoinType.AEON, 97 },
};

public static readonly Dictionary<CoinType, UInt64> AddressPrefix = new Dictionary<CoinType, UInt64>
{
{ CoinType.XMR, 18 },
{ CoinType.ETN, 18018 },
{ CoinType.AEON, 178 },
};

public static readonly Dictionary<CoinType, UInt64> AddressPrefixTestnet = new Dictionary<CoinType, UInt64>
{
{ CoinType.XMR, 53 },
{ CoinType.ETN, 53 },
{ CoinType.AEON, 178 },
};

public static readonly Dictionary<CoinType, UInt64> AddressPrefixIntegrated = new Dictionary<CoinType, UInt64>
{
{ CoinType.XMR, 19 },
{ CoinType.ETN, 18019 },
{ CoinType.AEON, 178 },
};

public static readonly Dictionary<CoinType, UInt64> AddressPrefixIntegratedTestnet = new Dictionary<CoinType, UInt64>
{
{ CoinType.XMR, 54 },
{ CoinType.ETN, 54 },
{ CoinType.AEON, 178 },
};

public static readonly Dictionary<CoinType, decimal> SmallestUnit = new Dictionary<CoinType, decimal>
{
{ CoinType.XMR, Piconero },
Expand All @@ -57,6 +86,7 @@ public class MoneroConstants
public const int MoneroRpcMethodNotFound = -32601;
public const char MainNetAddressPrefix = '4';
public const char TestNetAddressPrefix = '9';
public const int PaymentIdHexLength = 64;
public static readonly Regex RegexValidNonce = new Regex("^[0-9a-f]{8}$", RegexOptions.Compiled);

public static readonly BigInteger Diff1 = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16);
Expand Down
50 changes: 38 additions & 12 deletions src/MiningCore/Blockchain/Monero/MoneroJobManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using System.Security.Cryptography;
using System.Threading.Tasks;
using Autofac;
using Microsoft.Extensions.DependencyInjection;
using MiningCore.Blockchain.Bitcoin;
using MiningCore.Blockchain.Monero.DaemonRequests;
using MiningCore.Blockchain.Monero.DaemonResponses;
Expand Down Expand Up @@ -75,7 +76,7 @@ public MoneroJobManager(
private readonly NotificationService notificationService;
private readonly IMasterClock clock;
private MoneroNetworkType networkType;
private uint poolAddressBase58Prefix;
private UInt64 poolAddressBase58Prefix;
private DaemonEndpointConfig[] walletDaemonEndpoints;

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

poolAddressBase58Prefix = LibCryptonote.DecodeAddress(poolConfig.Address);
if (poolAddressBase58Prefix == 0)
logger.ThrowLogPoolStartupException("Unable to decode pool-address", LogCat);

// extract standard daemon endpoints
daemonEndpoints = poolConfig.Daemons
.Where(x => string.IsNullOrEmpty(x.Category))
Expand All @@ -222,12 +219,23 @@ public bool ValidateAddress(string address)
{
Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty");

if (address.Length != MoneroConstants.AddressLength[poolConfig.Coin.Type])
return false;

var addressPrefix = LibCryptonote.DecodeAddress(address);
if (addressPrefix != poolAddressBase58Prefix)
return false;
var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address);

switch (networkType)
{
case MoneroNetworkType.Main:
if (addressPrefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type] &&
addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type])
return false;
break;

case MoneroNetworkType.Test:
if (addressPrefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type] &&
addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type])
return false;
break;
}

return true;
}
Expand Down Expand Up @@ -255,9 +263,9 @@ public async Task<MoneroShare> SubmitShareAsync(StratumClient worker,
{
Contract.RequiresNonNull(worker, nameof(worker));
Contract.RequiresNonNull(request, nameof(request));
var context = worker.GetContextAs<MoneroWorkerContext>();

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

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

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

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

// address validation
poolAddressBase58Prefix = LibCryptonote.DecodeAddress(poolConfig.Address);
if (poolAddressBase58Prefix == 0)
logger.ThrowLogPoolStartupException("Unable to decode pool-address", LogCat);

switch(networkType)
{
case MoneroNetworkType.Main:
if(poolAddressBase58Prefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type])
logger.ThrowLogPoolStartupException($"Invalid pool address prefix. Expected {MoneroConstants.AddressPrefix[poolConfig.Coin.Type]}, got {poolAddressBase58Prefix}", LogCat);
break;

case MoneroNetworkType.Test:
if (poolAddressBase58Prefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type])
logger.ThrowLogPoolStartupException($"Invalid pool address prefix. Expected {MoneroConstants.AddressPrefix[poolConfig.Coin.Type]}, got {poolAddressBase58Prefix}", LogCat);
break;
}

ConfigureRewards();

// update stats
Expand Down
114 changes: 99 additions & 15 deletions src/MiningCore/Blockchain/Monero/MoneroPayoutHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
using MiningCore.Configuration;
using MiningCore.DaemonInterface;
using MiningCore.Extensions;
using MiningCore.Native;
using MiningCore.Notifications;
using MiningCore.Payments;
using MiningCore.Persistence;
Expand Down Expand Up @@ -139,10 +140,15 @@ private async Task PayoutBatch(Balance[] balances)
{
Destinations = balances
.Where(x => x.Amount > 0)
.Select(x => new TransferDestination
.Select(x =>
{
Address = x.Address,
Amount = (ulong) Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type])
ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId);

return new TransferDestination
{
Address = address,
Amount = (ulong) Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type])
};
}).ToArray(),

GetTxKey = true
Expand Down Expand Up @@ -190,10 +196,15 @@ private async Task PayoutBatch(Balance[] balances)
// update request
request.Destinations = page
.Where(x => x.Amount > 0)
.Select(x => new TransferDestination
.Select(x =>
{
Address = x.Address,
Amount = (ulong)Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type])
ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId);

return new TransferDestination
{
Address = address,
Amount = (ulong) Math.Floor(x.Amount * MoneroConstants.SmallestUnit[poolConfig.Coin.Type])
};
}).ToArray();

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

private async Task PayoutToPaymentId(Balance balance)
private void ExtractAddressAndPaymentId(string input, out string address, out string paymentId)
{
// extract paymentId
var address = (string) null;
var paymentId = (string) null;
paymentId = null;
var index = input.IndexOf(PayoutConstants.PayoutInfoSeperator);

var index = balance.Address.IndexOf(PayoutConstants.PayoutInfoSeperator);
if (index != -1)
{
paymentId = balance.Address.Substring(index + 1);
address = balance.Address.Substring(0, index);
address = input.Substring(0, index);

if (index + 1 < input.Length)
{
paymentId = input.Substring(index + 1);

// ignore invalid payment ids
if (paymentId.Length != MoneroConstants.PaymentIdHexLength)
paymentId = null;
}
}

else
address = input;
}

private async Task PayoutToPaymentId(Balance balance)
{
ExtractAddressAndPaymentId(balance.Address, out var address, out var paymentId);

if (string.IsNullOrEmpty(paymentId))
throw new InvalidOperationException("invalid paymentid");

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

// detect network
await GetNetworkTypeAsync();

// detect transfer_split support
var response = await walletDaemon.ExecuteCmdSingleAsync<TransferResponse>(MWC.TransferSplit);
walletSupportsTransferSplit = response.Error.Code != MoneroConstants.MoneroRpcMethodNotFound;
Expand Down Expand Up @@ -413,9 +441,65 @@ public async Task PayoutAsync(Balance[] balances)
#endif
}

// validate addresses
balances = balances
.Where(x =>
{
ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId);

var addressPrefix = LibCryptonote.DecodeAddress(address);
var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address);

switch (networkType)
{
case MoneroNetworkType.Main:
if (addressPrefix != MoneroConstants.AddressPrefix[poolConfig.Coin.Type] &&
addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type])
{
logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address {x.Address}");
return false;
}
break;

case MoneroNetworkType.Test:
if (addressPrefix != MoneroConstants.AddressPrefixTestnet[poolConfig.Coin.Type] &&
addressIntegratedPrefix != MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type])
{
logger.Warn(() => $"[{LogCategory}] Excluding payment to invalid address {x.Address}");
return false;
}
break;
}

return true;
})
.ToArray();

// simple balances first
var simpleBalances = balances
.Where(x => !x.Address.Contains(PayoutConstants.PayoutInfoSeperator))
.Where(x =>
{
ExtractAddressAndPaymentId(x.Address, out var address, out var paymentId);

var hasPaymentId = paymentId != null;
var isIntegratedAddress = false;
var addressIntegratedPrefix = LibCryptonote.DecodeIntegratedAddress(address);

switch (networkType)
{
case MoneroNetworkType.Main:
if (addressIntegratedPrefix == MoneroConstants.AddressPrefixIntegrated[poolConfig.Coin.Type])
isIntegratedAddress = true;
break;

case MoneroNetworkType.Test:
if (addressIntegratedPrefix == MoneroConstants.AddressPrefixIntegratedTestnet[poolConfig.Coin.Type])
isIntegratedAddress = true;
break;
}

return !hasPaymentId && !isIntegratedAddress;
})
.ToArray();

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

var paymentIdBalances = balances.Except(simpleBalances)
.Where(x => x.Address.Contains(PayoutConstants.PayoutInfoSeperator) && x.Amount >= minimumPaymentToPaymentId)
.Where(x => x.Amount >= minimumPaymentToPaymentId)
.ToArray();

foreach(var balance in paymentIdBalances)
Expand Down
2 changes: 1 addition & 1 deletion src/MiningCore/Blockchain/ZCash/ZCashJobManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public override async Task<BitcoinShare> SubmitShareAsync(StratumClient worker,

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

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

0 comments on commit 4172624

Please sign in to comment.