/** Check that all pools have a priority. */ private void checkPoolPriorities(List<Pool> pools) { for (Pool pool : pools) { if (pool.getPriority() == null) { pool.setPriority(getMinimumPoolPriority() + 1); } } }
/** * Compute the and set the current pool. Based on the pool priority and pool state. * * @param connection * @return */ @Override protected void computeCurrentPool() throws NoPoolAvailableException { List<Pool> pools = getProxyManager().getPools(); Pool newPool = null; checkPoolPriorities(pools); Collections.sort( pools, new Comparator<Pool>() { public int compare(Pool o1, Pool o2) { return o1.getPriority().compareTo(o2.getPriority()); } }); for (Pool pool : pools) { if (pool.isReady() && pool.isEnabled() && pool.isStable()) { newPool = pool; break; } } if (newPool == null) { throw new NoPoolAvailableException("No pool available. " + pools); } else { setCurrentPool(newPool); } }
/** Stop all pools */ public void stopPools() { synchronized (pools) { for (Pool pool : pools) { pool.stopPool("Proxy is shutting down!"); } } }
/** * Called when a pool send a notify request. * * @param pool * @param setDifficulty */ public void onPoolNotify(Pool pool, MiningNotifyNotification notify) { if (notify.getCleanJobs()) { LOGGER.info("New block detected on pool {}.", pool.getName()); } MiningNotifyNotification notification = new MiningNotifyNotification(); notification.setBitcoinVersion(notify.getBitcoinVersion()); notification.setCleanJobs(notify.getCleanJobs()); notification.setCoinbase1(notify.getCoinbase1()); notification.setCoinbase2(notify.getCoinbase2()); notification.setCurrentNTime(notify.getCurrentNTime()); notification.setJobId(notify.getJobId()); notification.setMerkleBranches(notify.getMerkleBranches()); notification.setNetworkDifficultyBits(notify.getNetworkDifficultyBits()); notification.setPreviousHash(notify.getPreviousHash()); Set<WorkerConnection> connections = getPoolWorkerConnections(pool); if (connections == null || connections.isEmpty()) { LOGGER.debug("No worker connections on pool {}. Do not send notify.", pool.getName()); } else { for (WorkerConnection connection : connections) { connection.onPoolNotify(notification); } } }
/** Called by pool when its state changes */ public void onPoolStateChange(Pool pool) { if (pool.isReady()) { LOGGER.warn("Pool {} is UP.", pool.getName()); poolSwitchingStrategyManager.onPoolUp(pool); } else { LOGGER.warn("Pool {} is DOWN. Moving connections to another one.", pool.getName()); poolSwitchingStrategyManager.onPoolDown(pool); } }
/** * Return the minimal priority over all pools. * * @param addPoolDTO * @return */ private int getMinimumPoolPriority() { int minPriority = 0; List<Pool> pools = getProxyManager().getPools(); for (Pool pool : pools) { if (pool.getPriority() > minPriority) { minPriority = pool.getPriority(); } } return minPriority; }
/** * Return the pool based on the pool name. * * @param poolHost * @return */ public Pool getPool(String poolName) { Pool result = null; synchronized (pools) { for (Pool pool : pools) { if (pool.getName().toString().equals(poolName)) { result = pool; break; } } } return result; }
/** * Disable/Enable the pool with the given name * * @param poolName * @param isEnabled * @throws NoPoolAvailableException */ public void setPoolEnabled(String poolName, boolean isEnabled) throws NoPoolAvailableException, Exception { Pool pool = getPool(poolName); if (pool == null) { throw new NoPoolAvailableException("Pool with name " + poolName + " is not found"); } if (pool.isEnabled() != isEnabled) { LOGGER.info("Set pool {} {}", pool.getName(), isEnabled ? "enabled" : "disabled"); pool.setEnabled(isEnabled, this); } }
@Override public void onPoolAdded(Pool pool) { // If the priority of the pool is set, check if it does not overlap // another pool with the same priority if (pool.getPriority() == null) { // Set by default the priority to the lowest over all pools. int minPriority = getMinimumPoolPriority(); pool.setPriority(minPriority + 1); } else { // If the priority is set, update the other pools. onPoolUpdated(pool); } super.onPoolAdded(pool); }
/** * To call when a subscribe request is received on a worker connection. Return the pool on which * the connection is bound. * * @param connection * @param request */ public Pool onSubscribeRequest(WorkerConnection connection, MiningSubscribeRequest request) throws NoPoolAvailableException { Pool pool = poolSwitchingStrategyManager.getPoolForConnection(connection); Set<WorkerConnection> workerConnections = getPoolWorkerConnections(pool); workerConnections.add(connection); this.workerConnections.add(connection); LOGGER.info( "New WorkerConnection {} subscribed. {} connections active on pool {}.", connection.getConnectionName(), workerConnections.size(), pool.getName()); return pool; }
/** Start all pools. */ public void startPools(List<Pool> pools) { this.pools = Collections.synchronizedList(new ArrayList<Pool>(pools)); synchronized (pools) { for (Pool pool : pools) { try { if (pool.isEnabled()) { pool.startPool(this); } else { LOGGER.warn("Do not start pool {} since it is disabled.", pool.getName()); } } catch (Exception e) { LOGGER.error("Failed to start the pool {}.", pool, e); } } } }
/** * Called when a pool set the difficulty. * * @param pool * @param setDifficulty */ public void onPoolSetDifficulty(Pool pool, MiningSetDifficultyNotification setDifficulty) { LOGGER.info("Set difficulty {} on pool {}.", setDifficulty.getDifficulty(), pool.getName()); MiningSetDifficultyNotification notification = new MiningSetDifficultyNotification(); notification.setDifficulty(setDifficulty.getDifficulty()); Set<WorkerConnection> connections = getPoolWorkerConnections(pool); if (connections == null || connections.isEmpty()) { LOGGER.debug("No worker connections on pool {}. Do not send setDifficulty.", pool.getName()); } else { for (WorkerConnection connection : connections) { connection.onPoolDifficultyChanged(notification); } } }
/** * Switch the given connection to the given pool. * * @param connection * @param newPool */ public void switchPoolForConnection(WorkerConnection connection, Pool newPool) throws TooManyWorkersException, ChangeExtranonceNotSupportedException { // If the old pool is the same as the new pool, do nothing. if (!newPool.equals(connection.getPool())) { // Remove the connection from the old pool connection list. Set<WorkerConnection> oldPoolConnections = getPoolWorkerConnections(connection.getPool()); if (oldPoolConnections != null) { oldPoolConnections.remove(connection); } // Then rebind the connection to this pool. An exception is thrown // if the rebind fails since the connection does not support the // extranonce change. connection.rebindToPool(newPool); // And finally add the worker connection to the pool's worker // connections Set<WorkerConnection> newPoolConnections = getPoolWorkerConnections(newPool); newPoolConnections.add(connection); // Ask to the pool to authorize the worker // Create a fake authorization request since when a connection is // rebound, the miner does not send auhtorization request (since it // has already done it). But it may be the first time this // connection is bound to this pool, so the username on this // connection is not yet authorized on the pool. for (Entry<String, String> entry : connection.getAuthorizedWorkers().entrySet()) { MiningAuthorizeRequest fakeRequest = new MiningAuthorizeRequest(); fakeRequest.setUsername(entry.getKey()); fakeRequest.setPassword(entry.getValue()); try { onAuthorizeRequest(connection, fakeRequest); } catch (AuthorizationException e) { LOGGER.error( "Authorization of user {} failed on pool {} when rebinding connection {}. Closing the connection. Cause: {}", entry.getKey(), newPool.getName(), connection.getConnectionName(), e.getMessage()); connection.close(); } } } }
/** * Remove the pool with the given name. * * @param poolName * @throws NoPoolAvailableException */ public void removePool(String poolName, Boolean keepHistory) throws NoPoolAvailableException { Pool pool = getPool(poolName); if (pool == null) { throw new NoPoolAvailableException("Pool with name " + poolName + " is not found"); } pool.stopPool("Pool removed"); pools.remove(pool); poolSwitchingStrategyManager.onPoolRemoved(pool); poolWorkerConnections.remove(pool); LOGGER.info("Pool {} removed.", poolName); // Remove the history if requested if (keepHistory != null && !keepHistory) { DatabaseManager.getInstance().deletePool(pool.getHost()); } }
/** * Set the priority of the pool with the given name and rebind worker connections based on this * new priority. * * @param poolName * @param newPriority * @throws BadParameterException */ public void setPoolPriority(String poolName, int newPriority) throws NoPoolAvailableException, BadParameterException { if (getPool(poolName) == null) { throw new NoPoolAvailableException("Pool with name " + poolName + " not found"); } if (newPriority < 0) { throw new BadParameterException("The priority has to be higher or equal to 0"); } Pool pool = getPool(poolName); LOGGER.info( "Changing pool {} priority from {} to {}.", pool.getName(), pool.getPriority(), newPriority); pool.setPriority(newPriority); poolSwitchingStrategyManager.onPoolUpdated(pool); }
/** * Called when a pool has sent a message to show. * * @param showMessage */ public void onPoolShowMessage(Pool pool, ClientShowMessageNotification showMessage) { LOGGER.info( "\n*****************************\nMessage from pool {}: {}\n*****************************", pool.getName(), showMessage.getMessage()); Set<WorkerConnection> connections = getPoolWorkerConnections(pool); if (connections != null && !connections.isEmpty()) { for (WorkerConnection connection : connections) { connection.onPoolShowMessage(showMessage); } } }
/** * Called when a pool set the extranonce * * @param pool * @param setExtranonce */ public void onPoolSetExtranonce(Pool pool, MiningSetExtranonceNotification setExtranonce) { LOGGER.info("Set the extranonce on pool {}.", pool.getName()); Set<WorkerConnection> connections = getPoolWorkerConnections(pool); if (connections == null || connections.isEmpty()) { LOGGER.debug("No worker connections on pool {}. Do not send setExtranonce.", pool.getName()); } else { for (WorkerConnection connection : connections) { try { connection.onPoolExtranonceChange(); } catch (ChangeExtranonceNotSupportedException e) { connection.close(); onWorkerDisconnection( connection, new Exception( "The workerConnection " + connection.getConnectionName() + " does not support setExtranonce notification.")); } } } }
@Override public void onPoolUpdated(Pool poolUpdated) { List<Pool> pools = getProxyManager().getPools(); checkPoolPriorities(pools); Collections.sort( pools, new Comparator<Pool>() { public int compare(Pool o1, Pool o2) { return o1.getPriority().compareTo(o2.getPriority()); } }); int newPriority = poolUpdated.getPriority(); int previousPriority = newPriority; for (Pool pool : pools) { // Move the priority of other pools with lower or // equals priority if (pool.getPriority() == previousPriority && !pool.equals(poolUpdated)) { pool.setPriority(pool.getPriority() + 1); previousPriority = pool.getPriority(); } } super.onPoolUpdated(poolUpdated); }
/** * @param poolToUpdate * @throws URISyntaxException * @throws PoolStartException * @throws SocketException * @throws BadParameterException */ public void updatePool(UpdatePoolDTO poolToUpdate) throws NotFoundException, SocketException, PoolStartException, URISyntaxException, BadParameterException { boolean hasBeenStopped = false; Pool pool = getPool(poolToUpdate.getName()); if (pool == null) { throw new NotFoundException( "The pool with name " + poolToUpdate.getName() + " has not been found."); } checkUpdatePoolParameters(poolToUpdate); if (poolToUpdate.getHost() != null && !poolToUpdate.getHost().equals(pool.getHost())) { if (!hasBeenStopped) { pool.stopPool("Pool updated and needed to restart."); } hasBeenStopped = true; pool.setHost(poolToUpdate.getHost()); } if (poolToUpdate.getIsExtranonceSubscribeEnabled() != null && !poolToUpdate .getIsExtranonceSubscribeEnabled() .equals(pool.getIsExtranonceSubscribeEnabled())) { if (!hasBeenStopped) { pool.stopPool("Pool updated and needed to restart."); } hasBeenStopped = true; pool.setIsExtranonceSubscribeEnabled(poolToUpdate.getIsExtranonceSubscribeEnabled()); } if (poolToUpdate.getPassword() != null && !poolToUpdate.getPassword().equals(pool.getPassword())) { if (!hasBeenStopped) { pool.stopPool("Pool updated and needed to restart."); } hasBeenStopped = true; pool.setPassword(poolToUpdate.getPassword()); } if (poolToUpdate.getUsername() != null && !poolToUpdate.getUsername().equals(pool.getUsername())) { if (!hasBeenStopped) { pool.stopPool("Pool updated and needed to restart."); } hasBeenStopped = true; pool.setUsername(poolToUpdate.getUsername()); } if (poolToUpdate.getPriority() != null && !poolToUpdate.getPriority().equals(pool.getPriority())) { if (poolToUpdate.getPriority() < 0) { throw new BadParameterException("The priority has to be higher or equal to 0"); } pool.setPriority(poolToUpdate.getPriority()); if (poolSwitchingStrategyManager != null) { poolSwitchingStrategyManager.onPoolUpdated(pool); } } if (poolToUpdate.getWeight() != null && !poolToUpdate.getWeight().equals(pool.getWeight())) { if (poolToUpdate.getWeight() < 0) { throw new BadParameterException("The weight has to be higher or equal to 0"); } pool.setWeight(poolToUpdate.getWeight()); if (poolSwitchingStrategyManager != null) { poolSwitchingStrategyManager.onPoolUpdated(pool); } } if (poolToUpdate.getAppendWorkerNames() != null && !poolToUpdate.getAppendWorkerNames().equals(pool.isAppendWorkerNames())) { if (!hasBeenStopped) { pool.stopPool("Pool updated and needed to restart."); } hasBeenStopped = true; pool.setAppendWorkerNames(poolToUpdate.getAppendWorkerNames()); } if (poolToUpdate.getWorkerNamesSeparator() != null && !poolToUpdate.getWorkerNamesSeparator().equals(pool.getWorkerSeparator())) { pool.setWorkerSeparator(poolToUpdate.getWorkerNamesSeparator()); } if (poolToUpdate.getUseWorkerPassword() != null && !poolToUpdate.getUseWorkerPassword().equals(pool.isUseWorkerPassword())) { pool.setUseWorkerPassword(poolToUpdate.getUseWorkerPassword()); } // If the pool has been stopped since some options needs a restart, // restart the pool. if (hasBeenStopped) { pool.startPool(this); } }
/** * Called when a pool is now stable. * * @param pool */ public void onPoolStable(Pool pool) { LOGGER.warn("Pool {} is STABLE.", pool.getName()); poolSwitchingStrategyManager.onPoolStable(pool); }
/** * Add the pool described in the given poolDTO * * @param addPoolDTO * @return * @throws URISyntaxException * @throws PoolStartException * @throws SocketException */ public Pool addPool(AddPoolDTO addPoolDTO) throws BadParameterException, SocketException, PoolStartException, URISyntaxException { LOGGER.debug("Trying to add pool {}.", addPoolDTO); checkAddPoolParameters(addPoolDTO); Pool poolToAdd = new Pool( addPoolDTO.getPoolName(), addPoolDTO.getPoolHost(), addPoolDTO.getUsername(), addPoolDTO.getPassword()); // By default, does not enable extranonce subscribe. poolToAdd.setExtranonceSubscribeEnabled( addPoolDTO.getEnableExtranonceSubscribe() != null && addPoolDTO.getEnableExtranonceSubscribe()); poolToAdd.setAppendWorkerNames( addPoolDTO.getAppendWorkerNames() != null ? addPoolDTO.getAppendWorkerNames() : false); poolToAdd.setWorkerSeparator( addPoolDTO.getWorkerNameSeparator() != null ? addPoolDTO.getWorkerNameSeparator() : Constants.DEFAULT_WORKER_NAME_SEPARTOR); poolToAdd.setUseWorkerPassword( addPoolDTO.getUseWorkerPassword() != null ? addPoolDTO.getUseWorkerPassword() : false); if (addPoolDTO.getPriority() != null) { poolToAdd.setPriority(addPoolDTO.getPriority()); } if (addPoolDTO.getWeight() != null) { poolToAdd.setWeight(addPoolDTO.getWeight()); } // Add the pool to the pool list pools.add(poolToAdd); if (addPoolDTO.getPriority() != null) { try { setPoolPriority(addPoolDTO.getPoolName(), addPoolDTO.getPriority()); } catch (NoPoolAvailableException e) { LOGGER.error("BUG DETECTED !!! This exceptin should not happen.", e); } } LOGGER.info("Pool added {}.", addPoolDTO); try { poolToAdd.setEnabled(addPoolDTO.getIsEnabled() == null || addPoolDTO.getIsEnabled()); } catch (Exception e) { throw new PoolStartException( "Failed to enable the created pool with name " + poolToAdd.getName() + ". This should not happen. Surely a BUUUUGGGG !!!!", e); } if (poolToAdd.isEnabled()) { poolToAdd.startPool(this); } poolSwitchingStrategyManager.onPoolAdded(poolToAdd); return poolToAdd; }