Dev (#180)
- Support ZMQ block notify for Bitcoin family. Fixes #151. - Adjust Lyra2v2 Hashrate multiplier. Fixes #168 - Made RPC polling optional - Updated configuration examples to be closer to real world usage - Added no RPC polling config example
This commit is contained in:
parent
5bd7574e3d
commit
2c62848d23
|
@ -8,7 +8,7 @@
|
|||
"perPoolLogFile": false
|
||||
},
|
||||
"banning": {
|
||||
"manager": "integrated"
|
||||
"manager": "integrated",
|
||||
"banOnJunkReceive": true,
|
||||
"banOnInvalidShares": false
|
||||
},
|
||||
|
@ -53,8 +53,8 @@
|
|||
"address": "XgmfWd5DXWGcYhxPcDPUJk44Cnh2e8tZk7",
|
||||
"percentage": 0
|
||||
}],
|
||||
"blockRefreshInterval": 1000,
|
||||
"jobRebroadcastTimeout": 55,
|
||||
"blockRefreshInterval": 500,
|
||||
"jobRebroadcastTimeout": 10,
|
||||
"clientConnectionTimeout": 600,
|
||||
"banning": {
|
||||
"enabled": true,
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
{
|
||||
"logging": {
|
||||
"level": "info",
|
||||
"enableConsoleLog": true,
|
||||
"enableConsoleColors": true,
|
||||
"logFile": "",
|
||||
"logBaseDirectory": "",
|
||||
"perPoolLogFile": false
|
||||
},
|
||||
"banning": {
|
||||
"manager": "integrated",
|
||||
"banOnJunkReceive": true,
|
||||
"banOnInvalidShares": false
|
||||
},
|
||||
"notifications": {
|
||||
"enabled": true,
|
||||
"email": {
|
||||
"host": "smtp.example.com",
|
||||
"port": 587,
|
||||
"user": "user",
|
||||
"password": "password",
|
||||
"fromAddress": "info@yourpool.org",
|
||||
"fromName": "support"
|
||||
},
|
||||
"admin": {
|
||||
"enabled": false,
|
||||
"emailAddress": "user@example.com",
|
||||
"notifyBlockFound": true
|
||||
}
|
||||
},
|
||||
"persistence": {
|
||||
"postgres": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 5432,
|
||||
"user": "miningcore",
|
||||
"password": "password",
|
||||
"database": "miningcore"
|
||||
}
|
||||
},
|
||||
"paymentProcessing": {
|
||||
"enabled": true,
|
||||
"interval": 600,
|
||||
"shareRecoveryFile": "recovered-shares.txt"
|
||||
},
|
||||
"pools": [{
|
||||
"id": "dash1",
|
||||
"enabled": true,
|
||||
"coin": {
|
||||
"type": "DASH"
|
||||
},
|
||||
"address": "XgmfWd5DXWGcYhxPcDPUJk44Cnh2e8tZk7",
|
||||
"rewardRecipients": [{
|
||||
"address": "XgmfWd5DXWGcYhxPcDPUJk44Cnh2e8tZk7",
|
||||
"percentage": 0
|
||||
}],
|
||||
"zmqBlockNotifySocket": "tcp://127.0.0.1:3000",
|
||||
"jobRebroadcastTimeout": 10,
|
||||
"clientConnectionTimeout": 600,
|
||||
"banning": {
|
||||
"enabled": true,
|
||||
"time": 600,
|
||||
"invalidPercent": 50,
|
||||
"checkThreshold": 50
|
||||
},
|
||||
"ports": {
|
||||
"3062": {
|
||||
"listenAddress": "0.0.0.0",
|
||||
"difficulty": 1024,
|
||||
"name": "ASIC Mining",
|
||||
"varDiff": {
|
||||
"minDiff": 512,
|
||||
"targetTime": 15,
|
||||
"retargetTime": 90,
|
||||
"variancePercent": 30
|
||||
}
|
||||
}
|
||||
},
|
||||
"daemons": [{
|
||||
"host": "127.0.0.1",
|
||||
"port": 9998,
|
||||
"user": "user",
|
||||
"password": "password"
|
||||
}],
|
||||
"paymentProcessing": {
|
||||
"enabled": true,
|
||||
"minimumPayment": 0.5,
|
||||
"payoutScheme": "PPLNS",
|
||||
"payoutSchemeConfig": {
|
||||
"factor": 2.0
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
"perPoolLogFile": false
|
||||
},
|
||||
"banning": {
|
||||
"manager": "integrated"
|
||||
"manager": "integrated",
|
||||
"banOnJunkReceive": true,
|
||||
"banOnInvalidShares": false
|
||||
},
|
||||
|
@ -56,8 +56,8 @@
|
|||
"percentage": 1.0
|
||||
}
|
||||
],
|
||||
"blockRefreshInterval": 1000,
|
||||
"jobRebroadcastTimeout": 55,
|
||||
"blockRefreshInterval": 500,
|
||||
"jobRebroadcastTimeout": 10,
|
||||
"clientConnectionTimeout": 600,
|
||||
"banning": {
|
||||
"enabled": true,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"perPoolLogFile": false
|
||||
},
|
||||
"banning": {
|
||||
"manager": "integrated"
|
||||
"manager": "integrated",
|
||||
"banOnJunkReceive": true,
|
||||
"banOnInvalidShares": false
|
||||
},
|
||||
|
@ -56,8 +56,8 @@
|
|||
"percentage": 1
|
||||
}
|
||||
],
|
||||
"blockRefreshInterval": 1000,
|
||||
"jobRebroadcastTimeout": 55,
|
||||
"blockRefreshInterval": 500,
|
||||
"jobRebroadcastTimeout": 10,
|
||||
"clientConnectionTimeout": 600,
|
||||
"banning": {
|
||||
"enabled": true,
|
||||
|
|
|
@ -54,8 +54,7 @@
|
|||
"address": "0x0942e9144606ad43f2e61a7ee332fe9914424712",
|
||||
"percentage": 0
|
||||
}],
|
||||
"blockRefreshInterval": 1000,
|
||||
"jobRebroadcastTimeout": 55,
|
||||
"blockRefreshInterval": 500,
|
||||
"clientConnectionTimeout": 600,
|
||||
"banning": {
|
||||
"enabled": true,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"perPoolLogFile": false
|
||||
},
|
||||
"banning": {
|
||||
"manager": "integrated"
|
||||
"manager": "integrated",
|
||||
"banOnJunkReceive": true,
|
||||
"banOnInvalidShares": false
|
||||
},
|
||||
|
@ -53,8 +53,8 @@
|
|||
"address": "LUWYwkz6DQLVeqJqHRtGjNhWUxBvBmE3SX",
|
||||
"percentage": 1
|
||||
}],
|
||||
"blockRefreshInterval": 1000,
|
||||
"jobRebroadcastTimeout": 55,
|
||||
"blockRefreshInterval": 500,
|
||||
"jobRebroadcastTimeout": 10,
|
||||
"clientConnectionTimeout": 600,
|
||||
"banning": {
|
||||
"enabled": true,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"perPoolLogFile": false
|
||||
},
|
||||
"banning": {
|
||||
"manager": "integrated"
|
||||
"manager": "integrated",
|
||||
"banOnJunkReceive": true,
|
||||
"banOnInvalidShares": false
|
||||
},
|
||||
|
@ -55,8 +55,8 @@
|
|||
"percentage": 1.0
|
||||
}
|
||||
],
|
||||
"blockRefreshInterval": 1000,
|
||||
"jobRebroadcastTimeout": 55,
|
||||
"blockRefreshInterval": 500,
|
||||
"jobRebroadcastTimeout": 10,
|
||||
"clientConnectionTimeout": 600,
|
||||
"banning": {
|
||||
"enabled": true,
|
||||
|
|
|
@ -55,8 +55,7 @@
|
|||
"percentage": 1
|
||||
}
|
||||
],
|
||||
"blockRefreshInterval": 1000,
|
||||
"jobRebroadcastTimeout": 55,
|
||||
"blockRefreshInterval": 500,
|
||||
"clientConnectionTimeout": 600,
|
||||
"banning": {
|
||||
"enabled": true,
|
||||
|
|
|
@ -66,8 +66,8 @@
|
|||
"percentage": 1.0
|
||||
}
|
||||
],
|
||||
"blockRefreshInterval": 1000,
|
||||
"jobRebroadcastTimeout": 55,
|
||||
"blockRefreshInterval": 500,
|
||||
"jobRebroadcastTimeout": 10,
|
||||
"clientConnectionTimeout": 600,
|
||||
"banning": {
|
||||
"enabled": true,
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"perPoolLogFile": false
|
||||
},
|
||||
"banning": {
|
||||
"manager": "integrated"
|
||||
"manager": "integrated",
|
||||
"banOnJunkReceive": true,
|
||||
"banOnInvalidShares": false
|
||||
},
|
||||
|
@ -58,8 +58,8 @@
|
|||
"percentage": 1.5
|
||||
}
|
||||
],
|
||||
"blockRefreshInterval": 1000,
|
||||
"jobRebroadcastTimeout": 55,
|
||||
"blockRefreshInterval": 500,
|
||||
"jobRebroadcastTimeout": 10,
|
||||
"clientConnectionTimeout": 600,
|
||||
"banning": {
|
||||
"enabled": true,
|
||||
|
|
|
@ -68,6 +68,11 @@ namespace MiningCore.Blockchain.Bitcoin
|
|||
public static double Pow2x32 = Math.Pow(2, 32);
|
||||
public static readonly BigInteger Diff1 = BigInteger.Parse("00ffff0000000000000000000000000000000000000000000000000000", NumberStyles.HexNumber);
|
||||
public const int CoinbaseMinConfimations = 102;
|
||||
|
||||
public const string ZmqPublisherTopicBlockHash = "hashblock";
|
||||
public const string ZmqPublisherTopicTxHash = "hashtx";
|
||||
public const string ZmqPublisherTopicBlockRaw = "rawblock";
|
||||
public const string ZmqPublisherTopicTxRaw = "rawtx";
|
||||
}
|
||||
|
||||
public class KnownAddresses
|
||||
|
|
|
@ -20,11 +20,16 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Autofac;
|
||||
using MiningCore.Blockchain.Bitcoin.Configuration;
|
||||
using MiningCore.Blockchain.Bitcoin.DaemonResponses;
|
||||
using MiningCore.Configuration;
|
||||
using MiningCore.Contracts;
|
||||
|
@ -33,11 +38,14 @@ using MiningCore.Crypto.Hashing.Algorithms;
|
|||
using MiningCore.Crypto.Hashing.Special;
|
||||
using MiningCore.DaemonInterface;
|
||||
using MiningCore.Extensions;
|
||||
using MiningCore.Mining;
|
||||
using MiningCore.Notifications;
|
||||
using MiningCore.Stratum;
|
||||
using MiningCore.Time;
|
||||
using MiningCore.Util;
|
||||
using NBitcoin;
|
||||
using NetMQ;
|
||||
using NetMQ.Sockets;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
@ -68,6 +76,7 @@ namespace MiningCore.Blockchain.Bitcoin
|
|||
protected readonly NotificationService notificationService;
|
||||
protected readonly IMasterClock clock;
|
||||
protected DaemonClient daemon;
|
||||
protected BitcoinPoolConfigExtra extraPoolConfig;
|
||||
protected readonly IExtraNonceProvider extraNonceProvider;
|
||||
protected const int ExtranonceBytes = 4;
|
||||
protected readonly IHashAlgorithm sha256d = new Sha256D();
|
||||
|
@ -98,30 +107,112 @@ namespace MiningCore.Blockchain.Bitcoin
|
|||
if (poolConfig.ExternalStratum)
|
||||
return;
|
||||
|
||||
jobRebroadcastTimeout = TimeSpan.FromSeconds(poolConfig.JobRebroadcastTimeout);
|
||||
jobRebroadcastTimeout = TimeSpan.FromSeconds(Math.Max(1, poolConfig.JobRebroadcastTimeout));
|
||||
|
||||
// periodically update block-template from daemon
|
||||
var newJobs = Observable.Interval(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval))
|
||||
.Select(_ => Observable.FromAsync(() => UpdateJob(false)))
|
||||
.Concat()
|
||||
.Do(isNew =>
|
||||
var sources = new List<IObservable<bool>>();
|
||||
var cancelTimeout = new List<IObservable<bool>>();
|
||||
|
||||
// block updates via ZMQ pub/sub
|
||||
var zmqPublisherSocket = extraPoolConfig?.ZmqBlockNotifySocket?.Trim();
|
||||
|
||||
if (!string.IsNullOrEmpty(zmqPublisherSocket))
|
||||
{
|
||||
var newJobsPubSub = Observable.Defer(()=> Observable.Create<bool>(obs =>
|
||||
{
|
||||
if (isNew)
|
||||
logger.Info(() => $"[{LogCat}] New block {currentJob.BlockTemplate.Height} detected");
|
||||
})
|
||||
.Where(isNew => isNew)
|
||||
var tcs = new CancellationTokenSource();
|
||||
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
while (!tcs.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var subSocket = new SubscriberSocket())
|
||||
{
|
||||
//subSocket.Options.ReceiveHighWatermark = 1000;
|
||||
subSocket.Connect(zmqPublisherSocket);
|
||||
subSocket.Subscribe(BitcoinConstants.ZmqPublisherTopicBlockHash);
|
||||
|
||||
logger.Info($"Subscribed to {zmqPublisherSocket}/{BitcoinConstants.ZmqPublisherTopicBlockHash} for ZMQ pub/sub block updates");
|
||||
|
||||
while (true)
|
||||
{
|
||||
subSocket.ReceiveMultipartMessage(2);
|
||||
//var msg = subSocket.ReceiveMultipartMessage(2);
|
||||
//var topic = msg.First().ConvertToString(Encoding.UTF8);
|
||||
//var body = msg.Last().ConvertToString(Encoding.UTF8);
|
||||
|
||||
obs.OnNext(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.Error(ex);
|
||||
}
|
||||
|
||||
// do not consume all CPU cycles in case of a long lasting error condition
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
}, tcs.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
|
||||
|
||||
return Disposable.Create(() =>
|
||||
{
|
||||
tcs.Cancel();
|
||||
});
|
||||
}))
|
||||
.Select(_ => Observable.FromAsync(() => UpdateJob(false, "ZMQ pub/sub")))
|
||||
.Concat()
|
||||
.Publish()
|
||||
.RefCount();
|
||||
|
||||
sources.Add(newJobsPubSub);
|
||||
cancelTimeout.Add(newJobsPubSub);
|
||||
}
|
||||
|
||||
if (poolConfig.BlockRefreshInterval > 0)
|
||||
{
|
||||
// periodically update block-template from daemon
|
||||
var newJobsPolled = Observable.Interval(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval))
|
||||
.Select(_ => Observable.FromAsync(() => UpdateJob(false, "RPC polling")))
|
||||
.Concat()
|
||||
.Where(isNew => isNew)
|
||||
.Publish()
|
||||
.RefCount();
|
||||
|
||||
sources.Add(newJobsPolled);
|
||||
cancelTimeout.Add(newJobsPolled);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// poll for the first successful update after which polling is suspended forever
|
||||
var newJobsPolled = Observable.Interval(TimeSpan.FromMilliseconds(poolConfig.BlockRefreshInterval))
|
||||
.Select(_ => Observable.FromAsync(() => UpdateJob(false, "RPC polling")))
|
||||
.Concat()
|
||||
.Where(isNew => isNew)
|
||||
.Take(1)
|
||||
.Publish()
|
||||
.RefCount();
|
||||
|
||||
sources.Add(newJobsPolled);
|
||||
cancelTimeout.Add(newJobsPolled);
|
||||
}
|
||||
|
||||
// if there haven't been any new jobs for a while, force an update
|
||||
var forcedNewJobs = Observable.Timer(jobRebroadcastTimeout)
|
||||
.TakeUntil(newJobs) // cancel timeout if an actual new job has been detected
|
||||
var cancelRebroadcast = cancelTimeout.Count > 0 ?
|
||||
cancelTimeout.Count > 1 ? Observable.Merge(cancelTimeout) : cancelTimeout.First() :
|
||||
Observable.Never<bool>();
|
||||
|
||||
sources.Add(Observable.Timer(jobRebroadcastTimeout)
|
||||
.TakeUntil(cancelRebroadcast) // cancel timeout if an actual new job has been detected
|
||||
.Do(_ => logger.Debug(() => $"[{LogCat}] No new blocks for {jobRebroadcastTimeout.TotalSeconds} seconds - updating transactions & rebroadcasting work"))
|
||||
.Select(x => Observable.FromAsync(() => UpdateJob(true)))
|
||||
.Concat()
|
||||
.Repeat();
|
||||
.Repeat());
|
||||
|
||||
Jobs = newJobs.Merge(forcedNewJobs)
|
||||
Jobs = Observable.Merge(sources)
|
||||
.Select(GetJobParamsForStratum);
|
||||
}
|
||||
|
||||
|
@ -380,6 +471,13 @@ namespace MiningCore.Blockchain.Bitcoin
|
|||
|
||||
protected override string LogCat => "Bitcoin Job Manager";
|
||||
|
||||
public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig)
|
||||
{
|
||||
extraPoolConfig = poolConfig.Extra.SafeExtensionDataAs<BitcoinPoolConfigExtra>();
|
||||
|
||||
base.Configure(poolConfig, clusterConfig);
|
||||
}
|
||||
|
||||
protected override void ConfigureDaemons()
|
||||
{
|
||||
var jsonSerializerSettings = ctx.Resolve<JsonSerializerSettings>();
|
||||
|
@ -540,7 +638,7 @@ namespace MiningCore.Blockchain.Bitcoin
|
|||
}
|
||||
}
|
||||
|
||||
protected virtual async Task<bool> UpdateJob(bool forceUpdate)
|
||||
protected virtual async Task<bool> UpdateJob(bool forceUpdate, string via = null)
|
||||
{
|
||||
logger.LogInvoke(LogCat);
|
||||
|
||||
|
@ -571,9 +669,12 @@ namespace MiningCore.Blockchain.Bitcoin
|
|||
ShareMultiplier,
|
||||
coinbaseHasher, headerHasher, blockHasher);
|
||||
|
||||
// update stats
|
||||
if (isNew)
|
||||
{
|
||||
if(via != null)
|
||||
logger.Info($"[{LogCat}] Detected new block {blockTemplate.Height} via {via}");
|
||||
|
||||
// update stats
|
||||
BlockchainStats.LastNetworkBlockTime = clock.Now;
|
||||
BlockchainStats.BlockHeight = blockTemplate.Height;
|
||||
BlockchainStats.NetworkDifficulty = job.Difficulty;
|
||||
|
|
|
@ -355,7 +355,7 @@ namespace MiningCore.Blockchain.Bitcoin
|
|||
|
||||
// OW: tmp hotfix
|
||||
if (poolConfig.Coin.Type == CoinType.MONA || poolConfig.Coin.Type == CoinType.VTC || poolConfig.Coin.Type == CoinType.STAK)
|
||||
result *= 2;
|
||||
result *= 4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -23,5 +23,11 @@ namespace MiningCore.Blockchain.Bitcoin.Configuration
|
|||
public class BitcoinPoolConfigExtra
|
||||
{
|
||||
public int? MinimumConfirmations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Address of ZeroMQ block notify socket
|
||||
/// Should match the value of -zmqpubhashblock daemon start parameter
|
||||
/// </summary>
|
||||
public string ZmqBlockNotifySocket { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Numerics;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -43,7 +40,6 @@ using MiningCore.Notifications;
|
|||
using MiningCore.Stratum;
|
||||
using MiningCore.Time;
|
||||
using MiningCore.Util;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
|
|
|
@ -37,14 +37,13 @@ namespace MiningCore.Blockchain.ZCash
|
|||
};
|
||||
}
|
||||
|
||||
private ZCashPoolConfigExtra poolExtraConfig;
|
||||
private ZCashPoolConfigExtra zcashExtraPoolConfig;
|
||||
|
||||
#region Overrides of JobManagerBase<TJob>
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig)
|
||||
{
|
||||
poolExtraConfig = poolConfig.Extra.SafeExtensionDataAs<ZCashPoolConfigExtra>();
|
||||
zcashExtraPoolConfig = poolConfig.Extra.SafeExtensionDataAs<ZCashPoolConfigExtra>();
|
||||
|
||||
base.Configure(poolConfig, clusterConfig);
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ namespace MiningCore.Configuration
|
|||
.WithMessage("You must provide the webhook url");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class NetworkEndpointConfigValidator<T> : AbstractValidator<T>
|
||||
where T : NetworkEndpointConfig
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue