- 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:
Oliver Weichhold 2018-01-15 17:59:38 +01:00 committed by GitHub
parent 5bd7574e3d
commit 2c62848d23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 247 additions and 49 deletions

View File

@ -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,

View File

@ -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
}
}
}]
}

View File

@ -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,

View File

@ -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,

View File

@ -54,8 +54,7 @@
"address": "0x0942e9144606ad43f2e61a7ee332fe9914424712",
"percentage": 0
}],
"blockRefreshInterval": 1000,
"jobRebroadcastTimeout": 55,
"blockRefreshInterval": 500,
"clientConnectionTimeout": 600,
"banning": {
"enabled": true,

View File

@ -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,

View File

@ -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,

View File

@ -55,8 +55,7 @@
"percentage": 1
}
],
"blockRefreshInterval": 1000,
"jobRebroadcastTimeout": 55,
"blockRefreshInterval": 500,
"clientConnectionTimeout": 600,
"banning": {
"enabled": true,

View File

@ -66,8 +66,8 @@
"percentage": 1.0
}
],
"blockRefreshInterval": 1000,
"jobRebroadcastTimeout": 55,
"blockRefreshInterval": 500,
"jobRebroadcastTimeout": 10,
"clientConnectionTimeout": 600,
"banning": {
"enabled": true,

View File

@ -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,

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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; }
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -70,7 +70,7 @@ namespace MiningCore.Configuration
.WithMessage("You must provide the webhook url");
}
}
public class NetworkEndpointConfigValidator<T> : AbstractValidator<T>
where T : NetworkEndpointConfig
{