Coverage Summary for Class: MinerClientImpl (co.rsk.mine)

Class Method, % Line, %
MinerClientImpl 0% (0/11) 0% (0/61)
MinerClientImpl$1 0% (0/2) 0% (0/5)
MinerClientImpl$RefreshWork 0% (0/2) 0% (0/7)
Total 0% (0/15) 0% (0/73)


1 /* 2  * This file is part of RskJ 3  * Copyright (C) 2017 RSK Labs Ltd. 4  * 5  * This program is free software: you can redistribute it and/or modify 6  * it under the terms of the GNU Lesser General Public License as published by 7  * the Free Software Foundation, either version 3 of the License, or 8  * (at your option) any later version. 9  * 10  * This program is distributed in the hope that it will be useful, 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13  * GNU Lesser General Public License for more details. 14  * 15  * You should have received a copy of the GNU Lesser General Public License 16  * along with this program. If not, see <http://www.gnu.org/licenses/>. 17  */ 18  19 package co.rsk.mine; 20  21 import co.rsk.net.NodeBlockProcessor; 22 import co.rsk.panic.PanicProcessor; 23 import org.ethereum.rpc.TypeConverter; 24 import org.slf4j.Logger; 25 import org.slf4j.LoggerFactory; 26  27 import javax.annotation.Nonnull; 28 import java.math.BigInteger; 29 import java.time.Duration; 30 import java.util.Timer; 31 import java.util.TimerTask; 32  33 /** 34  * MinerClient mines new blocks. 35  * In fact it just performs the proof-of-work needed to find a valid block and uses 36  * uses MinerServer to build blocks to mine and publish blocks once a valid nonce was found. 37  * @author Oscar Guindzberg 38  */ 39 public class MinerClientImpl implements MinerClient { 40  private long nextNonceToUse = 0; 41  42  private static final Logger logger = LoggerFactory.getLogger("minerClient"); 43  private static final PanicProcessor panicProcessor = new PanicProcessor(); 44  45  private final NodeBlockProcessor nodeBlockProcessor; 46  private final MinerServer minerServer; 47  private final Duration delayBetweenBlocks; 48  private final Duration delayBetweenRefreshes; 49  50  private volatile boolean stop = false; 51  52  private volatile boolean isMining = false; 53  54  private volatile boolean newBestBlockArrivedFromAnotherNode = false; 55  56  private volatile MinerWork work; 57  private Timer aTimer; 58  59  public MinerClientImpl(NodeBlockProcessor nodeBlockProcessor, MinerServer minerServer, Duration delayBetweenBlocks, Duration delayBetweenRefreshes) { 60  this.nodeBlockProcessor = nodeBlockProcessor; 61  this.minerServer = minerServer; 62  this.delayBetweenBlocks = delayBetweenBlocks; 63  this.delayBetweenRefreshes = delayBetweenRefreshes; 64  } 65  66  @Override 67  public void start() { 68  aTimer = new Timer("Refresh work for mining"); 69  aTimer.schedule(createRefreshWork(), 0, this.delayBetweenRefreshes.toMillis()); 70  71  Thread doWorkThread = this.createDoWorkThread(); 72  doWorkThread.start(); 73  } 74  75  public RefreshWork createRefreshWork() { 76  return new RefreshWork(); 77  } 78  79  public Thread createDoWorkThread() { 80  return new Thread("miner client") { 81  @Override 82  public void run() { 83  isMining = true; 84  85  while (!stop) { 86  doWork(); 87  } 88  89  isMining = false; 90  } 91  }; 92  } 93  94  public boolean isMining() { 95  return this.isMining; 96  } 97  98  public void doWork() { 99  try { 100  if (mineBlock()) { 101  if (!this.delayBetweenBlocks.isZero()) { 102  Thread.sleep(this.delayBetweenBlocks.toMillis()); 103  } 104  } 105  } catch (Exception e) { 106  logger.error("Error on mining", e); 107  panicProcessor.panic("mine", e.getMessage()); 108  } 109  } 110  111  @Override 112  public boolean mineBlock() { 113  if (this.nodeBlockProcessor != null) { 114  if (this.nodeBlockProcessor.hasBetterBlockToSync()) { 115  try { 116  Thread.sleep(10000); 117  } catch (InterruptedException ex) { 118  logger.error("Interrupted mining sleep", ex); 119  } 120  return false; 121  } 122  } 123  124  newBestBlockArrivedFromAnotherNode = false; 125  work = minerServer.getWork(); 126  127  co.rsk.bitcoinj.core.NetworkParameters bitcoinNetworkParameters = co.rsk.bitcoinj.params.RegTestParams.get(); 128  co.rsk.bitcoinj.core.BtcTransaction bitcoinMergedMiningCoinbaseTransaction = MinerUtils.getBitcoinMergedMiningCoinbaseTransaction(bitcoinNetworkParameters, work); 129  co.rsk.bitcoinj.core.BtcBlock bitcoinMergedMiningBlock = MinerUtils.getBitcoinMergedMiningBlock(bitcoinNetworkParameters, bitcoinMergedMiningCoinbaseTransaction); 130  131  BigInteger target = new BigInteger(1, TypeConverter.stringHexToByteArray(work.getTarget())); 132  boolean foundNonce = findNonce(bitcoinMergedMiningBlock, target); 133  134  if (newBestBlockArrivedFromAnotherNode) { 135  logger.info("Interrupted mining because another best block arrived"); 136  } 137  138  if (stop) { 139  logger.info("Interrupted mining because MinerClient was stopped"); 140  } 141  142  if (foundNonce) { 143  logger.info("Mined block: {}", work.getBlockHashForMergedMining()); 144  minerServer.submitBitcoinBlock(work.getBlockHashForMergedMining(), bitcoinMergedMiningBlock); 145  } 146  147  return foundNonce; 148  } 149  150  /** 151  * findNonce will try to find a valid nonce for bitcoinMergedMiningBlock, that satisfies the given target difficulty. 152  * 153  * @param bitcoinMergedMiningBlock bitcoinBlock to find nonce for. This block's nonce will be modified. 154  * @param target target difficulty. Block's hash should be lower than this number. 155  * @return true if a nonce was found, false otherwise. 156  * @remarks This method will return if the stop or newBetBlockArrivedFromAnotherNode intance variables are set to true. 157  */ 158  private boolean findNonce(@Nonnull final co.rsk.bitcoinj.core.BtcBlock bitcoinMergedMiningBlock, 159  @Nonnull final BigInteger target) { 160  bitcoinMergedMiningBlock.setNonce(nextNonceToUse++); 161  162  while (!stop && !newBestBlockArrivedFromAnotherNode) { 163  // Is our proof of work valid yet? 164  BigInteger blockHashBI = bitcoinMergedMiningBlock.getHash().toBigInteger(); 165  if (blockHashBI.compareTo(target) <= 0) { 166  return true; 167  } 168  // No, so increment the nonce and try again. 169  bitcoinMergedMiningBlock.setNonce(nextNonceToUse++); 170  if (bitcoinMergedMiningBlock.getNonce() % 100000 == 0) { 171  logger.debug("Solving block. Nonce: {}", bitcoinMergedMiningBlock.getNonce()); 172  } 173  } 174  175  return false; // couldn't find a valid nonce 176  } 177  178  @Override 179  public void stop() { 180  stop = true; 181  182  if (aTimer!=null) { 183  aTimer.cancel(); 184  } 185  } 186  187  /** 188  * RefreshWork asks the minerServer for new work. 189  */ 190  public class RefreshWork extends TimerTask { 191  @Override 192  public void run() { 193  MinerWork receivedWork = minerServer.getWork(); 194  MinerWork previousWork = work; 195  if (previousWork != null && receivedWork != null && 196  !receivedWork.getBlockHashForMergedMining().equals(previousWork.getBlockHashForMergedMining())) { 197  newBestBlockArrivedFromAnotherNode = true; 198  logger.debug("There is a new best block: {}", receivedWork.getBlockHashForMergedMining()); 199  } 200  } 201  } 202 }