public ShareRecorder(IConnectionFactory cf, IMapper mapper, JsonSerializerSettings jsonSerializerSettings, IShareRepository shareRepo, IBlockRepository blockRepo, IMasterClock clock, IMessageBus messageBus, NotificationService notificationService) { Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(mapper, nameof(mapper)); Contract.RequiresNonNull(shareRepo, nameof(shareRepo)); Contract.RequiresNonNull(blockRepo, nameof(blockRepo)); Contract.RequiresNonNull(jsonSerializerSettings, nameof(jsonSerializerSettings)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(messageBus, nameof(messageBus)); Contract.RequiresNonNull(notificationService, nameof(notificationService)); this.cf = cf; this.mapper = mapper; this.jsonSerializerSettings = jsonSerializerSettings; this.clock = clock; this.messageBus = messageBus; this.notificationService = notificationService; this.shareRepo = shareRepo; this.blockRepo = blockRepo; BuildFaultHandlingPolicy(); }
/// <summary> /// Executes the request against all configured demons and returns their responses as an array /// </summary> /// <typeparam name="TResponse"></typeparam> /// <param name="method"></param> /// <param name="payload"></param> /// <returns></returns> public async Task <DaemonResponse <TResponse>[]> ExecuteCmdAllAsync <TResponse>(string method, object payload = null, JsonSerializerSettings payloadJsonSerializerSettings = null) where TResponse : class { Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty"); logger.LogInvoke(new[] { method }); var tasks = endPoints.Select(endPoint => BuildRequestTask(endPoint, method, payload, payloadJsonSerializerSettings)).ToArray(); try { await Task.WhenAll(tasks); } catch (Exception) { // ignored } var results = tasks.Select((x, i) => MapDaemonResponse <TResponse>(i, x)) .ToArray(); return(results); }
protected PayoutHandlerBase(IConnectionFactory cf, IMapper mapper, IShareRepository shareRepo, IBlockRepository blockRepo, IBalanceRepository balanceRepo, IPaymentRepository paymentRepo, IMasterClock clock, NotificationService notificationService) { Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(mapper, nameof(mapper)); Contract.RequiresNonNull(shareRepo, nameof(shareRepo)); Contract.RequiresNonNull(blockRepo, nameof(blockRepo)); Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); Contract.RequiresNonNull(paymentRepo, nameof(paymentRepo)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(notificationService, nameof(notificationService)); this.cf = cf; this.mapper = mapper; this.clock = clock; this.shareRepo = shareRepo; this.blockRepo = blockRepo; this.balanceRepo = balanceRepo; this.paymentRepo = paymentRepo; this.notificationService = notificationService; BuildFaultHandlingPolicy(); }
public void Ban(IPAddress address, TimeSpan duration) { Contract.RequiresNonNull(address, nameof(address)); Contract.Requires <ArgumentException>(duration.TotalMilliseconds > 0, $"{nameof(duration)} must not be empty"); cache.Set(address.ToString(), string.Empty, duration); }
public void Configure(DaemonEndpointConfig[] endPoints, string digestAuthRealm = null) { Contract.RequiresNonNull(endPoints, nameof(endPoints)); Contract.Requires <ArgumentException>(endPoints.Length > 0, $"{nameof(endPoints)} must not be empty"); this.endPoints = endPoints; // create one HttpClient instance per endpoint that carries the associated credentials httpClients = endPoints.ToDictionary(endpoint => endpoint, endpoint => { var handler = new HttpClientHandler { Credentials = new NetworkCredential(endpoint.User, endpoint.Password), PreAuthenticate = true, AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip }; if (endpoint.Ssl && !endpoint.ValidateCert) { handler.ClientCertificateOptions = ClientCertificateOption.Manual; handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true; } return(new HttpClient(handler)); }); }
protected PoolBase(IComponentContext ctx, JsonSerializerSettings serializerSettings, IConnectionFactory cf, IStatsRepository statsRepo, IMapper mapper, IMasterClock clock, IMessageBus messageBus, NotificationService notificationService) : base(ctx, clock) { Contract.RequiresNonNull(ctx, nameof(ctx)); Contract.RequiresNonNull(serializerSettings, nameof(serializerSettings)); Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(statsRepo, nameof(statsRepo)); Contract.RequiresNonNull(mapper, nameof(mapper)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(messageBus, nameof(messageBus)); Contract.RequiresNonNull(notificationService, nameof(notificationService)); this.serializerSettings = serializerSettings; this.cf = cf; this.statsRepo = statsRepo; this.mapper = mapper; this.messageBus = messageBus; this.notificationService = notificationService; }
protected StratumServer(IComponentContext ctx, IMasterClock clock) { Contract.RequiresNonNull(ctx, nameof(ctx)); Contract.RequiresNonNull(clock, nameof(clock)); this.ctx = ctx; this.clock = clock; }
public bool IsBanned(IPAddress address) { Contract.RequiresNonNull(address, nameof(address)); var result = cache.Get(address.ToString()); return(result != null); }
public virtual void Configure(PoolConfig poolConfig, ClusterConfig clusterConfig) { Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); logger = LogUtil.GetPoolScopedLogger(typeof(PoolBase), poolConfig); this.poolConfig = poolConfig; this.clusterConfig = clusterConfig; }
public void Start(ClusterConfig clusterConfig) { Contract.RequiresNonNull(clusterConfig, nameof(clusterConfig)); this.clusterConfig = clusterConfig; logger.Info(() => $"Launching ..."); StartApi(clusterConfig); StartAdminApi(clusterConfig); }
public bool ValidateAddress(string address) { Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(address), $"{nameof(address)} must not be empty"); if (EthereumConstants.ZeroHashPattern.IsMatch(address) || !EthereumConstants.ValidAddressPattern.IsMatch(address)) return false; return true; }
public DaemonClient(JsonSerializerSettings serializerSettings) { Contract.RequiresNonNull(serializerSettings, nameof(serializerSettings)); this.serializerSettings = serializerSettings; serializer = new JsonSerializer { ContractResolver = serializerSettings.ContractResolver }; }
public byte[] WithFirst(byte[] first) { Contract.RequiresNonNull(first, nameof(first)); foreach (var step in Steps) { first = DoubleDigest(first.Concat(step)).ToArray(); } return(first); }
public async Task StartAsync(CancellationToken ct) { Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); logger.Info(() => $"[{LogCat}] Launching ..."); await StartDaemonAsync(ct); await EnsureDaemonsSynchedAsync(ct); await PostStartInitAsync(ct); logger.Info(() => $"[{LogCat}] Online"); }
/// <summary> /// Executes the requests against all configured demons and returns the first successful response array /// </summary> /// <returns></returns> public async Task <DaemonResponse <JToken>[]> ExecuteBatchAnyAsync(params DaemonCmd[] batch) { Contract.RequiresNonNull(batch, nameof(batch)); logger.LogInvoke(batch.Select(x => x.Method).ToArray()); var tasks = endPoints.Select(endPoint => BuildBatchRequestTask(endPoint, batch)).ToArray(); var taskFirstCompleted = await Task.WhenAny(tasks); var result = MapDaemonBatchResponse(0, taskFirstCompleted); return(result); }
public ApiServer( IMapper mapper, IConnectionFactory cf, IBlockRepository blocksRepo, IPaymentRepository paymentsRepo, IStatsRepository statsRepo, IShareRepository shareRepo, IMasterClock clock, IMessageBus messageBus) { Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(statsRepo, nameof(statsRepo)); Contract.RequiresNonNull(statsRepo, nameof(statsRepo)); Contract.RequiresNonNull(blocksRepo, nameof(blocksRepo)); Contract.RequiresNonNull(paymentsRepo, nameof(paymentsRepo)); Contract.RequiresNonNull(mapper, nameof(mapper)); Contract.RequiresNonNull(clock, nameof(clock)); Contract.RequiresNonNull(messageBus, nameof(messageBus)); this.cf = cf; this.statsRepo = statsRepo; this.shareRepo = shareRepo; this.blocksRepo = blocksRepo; this.paymentsRepo = paymentsRepo; this.mapper = mapper; this.clock = clock; messageBus.Listen <BlockNotification>().Subscribe(OnBlockNotification); requestMap = new Dictionary <Regex, Func <HttpContext, Match, Task> > { { new Regex("^/api/pools$", RegexOptions.Compiled), GetPoolInfosAsync }, { new Regex("^/api/pools/(?<poolId>[^/]+)/performance$", RegexOptions.Compiled), GetPoolPerformanceAsync }, { new Regex("^/api/pools/(?<poolId>[^/]+)/miners$", RegexOptions.Compiled), PagePoolMinersAsync }, { new Regex("^/api/pools/(?<poolId>[^/]+)/blocks$", RegexOptions.Compiled), PagePoolBlocksPagedAsync }, { new Regex("^/api/pools/(?<poolId>[^/]+)/payments$", RegexOptions.Compiled), PagePoolPaymentsAsync }, { new Regex("^/api/pools/(?<poolId>[^/]+)$", RegexOptions.Compiled), GetPoolInfoAsync }, { new Regex("^/api/pools/(?<poolId>[^/]+)/miners/(?<address>[^/]+)/payments$", RegexOptions.Compiled), PageMinerPaymentsAsync }, { new Regex("^/api/pools/(?<poolId>[^/]+)/miners/(?<address>[^/]+)/balancechanges$", RegexOptions.Compiled), PageMinerBalanceChangesAsync }, { new Regex("^/api/pools/(?<poolId>[^/]+)/miners/(?<address>[^/]+)/performance$", RegexOptions.Compiled), GetMinerPerformanceAsync }, { new Regex("^/api/pools/(?<poolId>[^/]+)/miners/(?<address>[^/]+)$", RegexOptions.Compiled), GetMinerInfoAsync }, }; requestMapAdmin = new Dictionary <Regex, Func <HttpContext, Match, Task> > { { new Regex("^/api/admin/forcegc$", RegexOptions.Compiled), HandleForceGcAsync }, { new Regex("^/api/admin/stats/gc$", RegexOptions.Compiled), HandleGcStatsAsync }, }; }
public SoloPaymentScheme(IConnectionFactory cf, IShareRepository shareRepo, IBlockRepository blockRepo, IBalanceRepository balanceRepo) { Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(shareRepo, nameof(shareRepo)); Contract.RequiresNonNull(blockRepo, nameof(blockRepo)); Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); this.cf = cf; this.shareRepo = shareRepo; this.blockRepo = blockRepo; this.balanceRepo = balanceRepo; }
/// <summary> /// Executes the request against all configured demons and returns the first successful response /// </summary> /// <typeparam name="TResponse"></typeparam> /// <param name="method"></param> /// <param name="payload"></param> /// <returns></returns> public async Task <DaemonResponse <TResponse> > ExecuteCmdSingleAsync <TResponse>(string method, object payload = null, JsonSerializerSettings payloadJsonSerializerSettings = null) where TResponse : class { Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty"); logger.LogInvoke(new[] { method }); var task = BuildRequestTask(endPoints.First(), method, payload, payloadJsonSerializerSettings); await task; var result = MapDaemonResponse <TResponse>(0, task); return(result); }
/// <summary> /// </summary> /// <example> /// python: http://runnable.com/U3jqtyYUmAUxtsSS/bitcoin-block-merkle-root-python /// nodejs: https://github.com/zone117x/node-stratum-pool/blob/master/lib/merkleTree.js#L9 /// </example> /// <param name="hashList"></param> /// <returns></returns> private IList <byte[]> CalculateSteps(IEnumerable <byte[]> hashList) { Contract.RequiresNonNull(hashList, nameof(hashList)); var steps = new List <byte[]>(); var L = new List <byte[]> { null }; L.AddRange(hashList); var startL = 2; var Ll = L.Count; if (Ll > 1) { while (true) { if (Ll == 1) { break; } steps.Add(L[1]); if (Ll % 2 == 1) { L.Add(L[L.Count - 1]); } var Ld = new List <byte[]>(); //foreach (int i in Range.From(startL).To(Ll).WithStepSize(2)) for (var i = startL; i < Ll; i += 2) { Ld.Add(MerkleJoin(L[i], L[i + 1])); } L = new List <byte[]> { null }; L.AddRange(Ld); Ll = L.Count; } } return(steps); }
/// <summary> /// Executes the request against all configured demons and returns the first successful response /// </summary> /// <typeparam name="TResponse"></typeparam> /// <returns></returns> public async Task <DaemonResponse <TResponse> > ExecuteCmdAnyAsync <TResponse>(string method, object payload = null, JsonSerializerSettings payloadJsonSerializerSettings = null, bool throwOnError = false) where TResponse : class { Contract.Requires <ArgumentException>(!string.IsNullOrEmpty(method), $"{nameof(method)} must not be empty"); logger.LogInvoke(new[] { method }); var tasks = endPoints.Select(endPoint => BuildRequestTask(endPoint, method, payload, payloadJsonSerializerSettings)).ToArray(); var taskFirstCompleted = await Task.WhenAny(tasks); var result = MapDaemonResponse <TResponse>(0, taskFirstCompleted, throwOnError); return(result); }
public PPLNSPaymentScheme(IConnectionFactory cf, IShareRepository shareRepo, IBlockRepository blockRepo, IBalanceRepository balanceRepo) { Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(shareRepo, nameof(shareRepo)); Contract.RequiresNonNull(blockRepo, nameof(blockRepo)); Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); this.cf = cf; this.shareRepo = shareRepo; this.blockRepo = blockRepo; this.balanceRepo = balanceRepo; BuildFaultHandlingPolicy(); }
public EthereumPayoutHandler( IComponentContext ctx, IConnectionFactory cf, IMapper mapper, IShareRepository shareRepo, IBlockRepository blockRepo, IBalanceRepository balanceRepo, IPaymentRepository paymentRepo, IMasterClock clock, NotificationService notificationService) : base(cf, mapper, shareRepo, blockRepo, balanceRepo, paymentRepo, clock, notificationService) { Contract.RequiresNonNull(ctx, nameof(ctx)); Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); Contract.RequiresNonNull(paymentRepo, nameof(paymentRepo)); this.ctx = ctx; }
public virtual async Task StartAsync(CancellationToken ct) { Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); logger.Info(() => $"[{LogCat}] Launching ..."); try { SetupBanning(clusterConfig); await SetupJobManager(ct); InitStats(); if (poolConfig.EnableInternalStratum == true) { var ipEndpoints = poolConfig.Ports.Keys .Select(port => PoolEndpoint2IPEndpoint(port, poolConfig.Ports[port])) .ToArray(); StartListeners(poolConfig.Id, ipEndpoints); } logger.Info(() => $"[{LogCat}] Online"); OutputPoolInfo(); } catch (PoolStartupAbortException) { // just forward these throw; } catch (TaskCanceledException) { // just forward these throw; } catch (Exception ex) { logger.Error(ex); throw; } }
public EthereumJobManager( IComponentContext ctx, NotificationService notificationService, IMasterClock clock, JsonSerializerSettings serializerSettings) : base(ctx) { Contract.RequiresNonNull(ctx, nameof(ctx)); Contract.RequiresNonNull(notificationService, nameof(notificationService)); Contract.RequiresNonNull(clock, nameof(clock)); this.clock = clock; this.notificationService = notificationService; serializer = new JsonSerializer { ContractResolver = serializerSettings.ContractResolver }; }
public PayoutManager(IComponentContext ctx, IConnectionFactory cf, IBlockRepository blockRepo, IShareRepository shareRepo, IBalanceRepository balanceRepo, NotificationService notificationService) { Contract.RequiresNonNull(ctx, nameof(ctx)); Contract.RequiresNonNull(cf, nameof(cf)); Contract.RequiresNonNull(blockRepo, nameof(blockRepo)); Contract.RequiresNonNull(shareRepo, nameof(shareRepo)); Contract.RequiresNonNull(balanceRepo, nameof(balanceRepo)); Contract.RequiresNonNull(notificationService, nameof(notificationService)); this.ctx = ctx; this.cf = cf; this.blockRepo = blockRepo; this.shareRepo = shareRepo; this.balanceRepo = balanceRepo; this.notificationService = notificationService; }
public async Task <Block[]> ClassifyBlocksAsync(Block[] blocks) { Contract.RequiresNonNull(poolConfig, nameof(poolConfig)); Contract.RequiresNonNull(blocks, nameof(blocks)); var pageSize = 100; var pageCount = (int)Math.Ceiling(blocks.Length / (double)pageSize); var blockCache = new Dictionary <long, DaemonResponses.Block>(); var result = new List <Block>(); for (var i = 0; i < pageCount; i++) { // get a page full of blocks var page = blocks .Skip(i * pageSize) .Take(pageSize) .ToArray(); // get latest block var latestBlockResponses = await daemon.ExecuteCmdAllAsync <DaemonResponses.Block>(EthCommands.GetBlockByNumber, new[] { (object)"latest", true }); var latestBlockHeight = latestBlockResponses.First(x => x.Error == null && x.Response?.Height != null).Response.Height.Value; // execute batch var blockInfos = await FetchBlocks(blockCache, page.Select(block => (long)block.BlockHeight).ToArray()); for (var j = 0; j < blockInfos.Length; j++) { var blockInfo = blockInfos[j]; var block = page[j]; // extract confirmation data from stored block var mixHash = block.TransactionConfirmationData.Split(":").First(); var nonce = block.TransactionConfirmationData.Split(":").Last(); // update progress block.ConfirmationProgress = Math.Min(1.0d, (double)(latestBlockHeight - block.BlockHeight) / EthereumConstants.MinConfimations); result.Add(block); // is it block mined by us? if (blockInfo.Miner == poolConfig.Address) { // mature? if (latestBlockHeight - block.BlockHeight >= EthereumConstants.MinConfimations) { block.Status = BlockStatus.Confirmed; block.ConfirmationProgress = 1; block.Reward = GetBaseBlockReward(chainType, block.BlockHeight); // base reward if (extraConfig?.KeepUncles == false) { block.Reward += blockInfo.Uncles.Length * (block.Reward / 32); // uncle rewards } if (extraConfig?.KeepTransactionFees == false && blockInfo.Transactions?.Length > 0) { block.Reward += await GetTxRewardAsync(blockInfo); // tx fees } logger.Info(() => $"[{LogCategory}] Unlocked block {block.BlockHeight} worth {FormatAmount(block.Reward)}"); } continue; } // search for a block containing our block as an uncle by checking N blocks in either direction var heightMin = block.BlockHeight - BlockSearchOffset; var heightMax = Math.Min(block.BlockHeight + BlockSearchOffset, latestBlockHeight); var range = new List <long>(); for (var k = heightMin; k < heightMax; k++) { range.Add((long)k); } // execute batch var blockInfo2s = await FetchBlocks(blockCache, range.ToArray()); foreach (var blockInfo2 in blockInfo2s) { // don't give up yet, there might be an uncle if (blockInfo2.Uncles.Length > 0) { // fetch all uncles in a single RPC batch request var uncleBatch = blockInfo2.Uncles.Select((x, index) => new DaemonCmd(EthCommands.GetUncleByBlockNumberAndIndex, new[] { blockInfo2.Height.Value.ToStringHexWithPrefix(), index.ToStringHexWithPrefix() })) .ToArray(); logger.Info(() => $"[{LogCategory}] Fetching {blockInfo2.Uncles.Length} uncles for block {blockInfo2.Height}"); var uncleResponses = await daemon.ExecuteBatchAnyAsync(uncleBatch); logger.Info(() => $"[{LogCategory}] Fetched {uncleResponses.Count(x => x.Error == null && x.Response != null)} uncles for block {blockInfo2.Height}"); var uncle = uncleResponses.Where(x => x.Error == null && x.Response != null) .Select(x => x.Response.ToObject <DaemonResponses.Block>()) .FirstOrDefault(x => x.Miner == poolConfig.Address); if (uncle != null) { // mature? if (latestBlockHeight - uncle.Height.Value >= EthereumConstants.MinConfimations) { block.Status = BlockStatus.Confirmed; block.ConfirmationProgress = 1; block.Reward = GetUncleReward(chainType, uncle.Height.Value, blockInfo2.Height.Value); block.BlockHeight = uncle.Height.Value; block.Type = EthereumConstants.BlockTypeUncle; logger.Info(() => $"[{LogCategory}] Unlocked uncle for block {blockInfo2.Height.Value} at height {uncle.Height.Value} worth {FormatAmount(block.Reward)}"); } else { logger.Info(() => $"[{LogCategory}] Got immature matching uncle for block {blockInfo2.Height.Value}. Will try again."); } break; } } } if (block.Status == BlockStatus.Pending && block.ConfirmationProgress > 0.10) { // we've lost this one block.Status = BlockStatus.Orphaned; block.Reward = 0; } } } return(result.ToArray()); }
public async Task<Share> SubmitShareAsync(StratumClient worker, string[] request, double stratumDifficulty, double stratumDifficultyBase) { Contract.RequiresNonNull(worker, nameof(worker)); Contract.RequiresNonNull(request, nameof(request)); logger.LogInvoke(LogCat, new[] { worker.ConnectionId }); var context = worker.GetContextAs<EthereumWorkerContext>(); EthereumJob job; string miner, jobId, nonce = string.Empty; if (context.IsNiceHashClient) { jobId = request[1]; nonce = request[2]; miner = request[0]; // stale? lock (jobLock) { var jobResult = validJobs.Where(x => x.Value.Id == jobId).FirstOrDefault(); if (jobResult.Value == null) throw new StratumException(StratumError.MinusOne, "stale share for job: " + jobId + "and nonce " + nonce); job = jobResult.Value; } } else { jobId = request[1]; nonce = request[0]; // stale? lock (jobLock) { var jobResult = validJobs.Where(x => x.Value.BlockTemplate.Header == jobId).FirstOrDefault(); if (jobResult.Value == null) throw new StratumException(StratumError.MinusOne, "stale share for job: " + jobId + "and nonce " + nonce); job = jobResult.Value; } } // validate & process var (share, fullNonceHex, headerHash, mixHash) = await job.ProcessShareAsync(worker, nonce, ethash); // enrich share with common data share.PoolId = poolConfig.Id; share.NetworkDifficulty = BlockchainStats.NetworkDifficulty; share.Source = clusterConfig.ClusterName; share.Created = clock.Now; // if block candidate, submit & check if accepted by network if (share.IsBlockCandidate) { logger.Info(() => $"[{LogCat}] Submitting block {share.BlockHeight}"); share.IsBlockCandidate = await SubmitBlockAsync(share, fullNonceHex.StartsWith("0x") ? fullNonceHex : $"0x{fullNonceHex}", headerHash, mixHash); if (share.IsBlockCandidate) { logger.Info(() => $"[{LogCat}] Daemon accepted block {share.BlockHeight} submitted by {context.MinerName}"); } } return share; }
protected JobManagerBase(IComponentContext ctx) { Contract.RequiresNonNull(ctx, nameof(ctx)); this.ctx = ctx; }