signum-node: https://github.com/signum-network/signum-node.git
signum-miner: https://github.com/signum-network/signum-miner.git
signum-plot:https://github.com/signum-network/signum-plotter.git
btdex: https://github.com/btdex/btdex.git
signum-node是一个全节点,存放有全部的区块信息
signum-miner实现挖矿逻辑,
btdex 是挖矿界面,会调用signum-miner实现挖矿逻辑。
class MineThread extends Thread {
public void run() {
try {
String cmd = minerFile.getAbsolutePath() + " -c " + MINER_CONFIG_FILE;
logger.info("Running miner with '{}'", cmd);
minerProcess = Runtime.getRuntime().exec(cmd, null, minerFile.getParentFile());
InputStream stdIn = minerProcess.getInputStream();
InputStreamReader isr = new InputStreamReader(stdIn);
BufferedReader br = new BufferedReader(isr);
while (minerProcess.isAlive()) {
String line = br.readLine();
if(line != null)
addToConsole(MINER_APP, line);
}
mining = false;
minerFile.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
};
btdex 调用miner下的二进制文件signum-miner,signum-miner实现具体的挖矿流程
create_cpu_worker_task->hash->find_best_deadline_rust
pub fn find_best_deadline_rust(
data: &[u8],
number_of_nonces: u64,
gensig: &[u8; 32],
) -> (u64, u64) {
let mut best_deadline = u64::MAX;
let mut best_offset = 0;
for i in 0..number_of_nonces as usize {
let result =
shabal256_deadline_fast(&data[i * SCOOP_SIZE..i * SCOOP_SIZE + SCOOP_SIZE], &gensig);
if result < best_deadline {
best_deadline = result;
best_offset = i;
}
}
(best_deadline, best_offset as u64)
}
plot
String plotterName = "signum-plotter";
if(OS.isWindows())
plotterName += ".exe";
else if(OS.isMacOsX())
plotterName += ".app";
plotterFile = new File(TMP_DIR, plotterName);
if (!plotterFile.exists() || plotterFile.length() == 0) {
InputStream link = (getClass().getResourceAsStream("/plotter/" + plotterName));
try {
logger.info("Copying ploter to {}", plotterFile.getAbsolutePath());
Files.copy(link, plotterFile.getAbsoluteFile().toPath());
if(!OS.isWindows())
plotterFile.setExecutable(true);
} catch (IOException ex) {
ex.printStackTrace();
Toast.makeText((JFrame) SwingUtilities.getWindowAncestor(this), ex.getMessage(), Toast.Style.ERROR).display();
plotting = false;
return;
}
}
PlotThread plotThread = new PlotThread();
plotThread.start();
1 signum-miner扫描磁盘找到合适的nonce和deadline,向节点提交nonce
let mut query = format!(
"requestType=submitNonce&accountId={}&nonce={}&secretPhrase={}&blockheight={}",
submission_data.account_id, submission_data.nonce, secret_phrase, submission_data.height
);
// If we don't have a secret phrase then we most likely talk to a pool or a proxy.
// Both can make use of the deadline, e.g. a proxy won't validate deadlines but still
// needs to rank the deadlines.
// The best thing is that legacy proxies use the unadjusted deadlines so...
// yay another parameter!
if secret_phrase == "" {
query += &format!("&deadline={}", submission_data.deadline_unadjusted);
}
2 节点维护了generators结合,这个集合存储使用这个节点出块的accountID,私钥,块高,这个数据结构可以根据nonce计算deadline节点收到submitNonce后,会调用addNonce方法
/src/brs/GeneratorImpl.java
private final Long accountId;
private final String secretPhrase;
private final byte[] publicKey;
private final BigInteger deadline;
private final BigInteger hit;
private final long baseTarget;
private final long nonce;
private final long block;
/src/brs/GeneratorImpl.java
public GeneratorState addNonce(String secretPhrase, Long nonce, byte[] publicKey) {
byte[] publicKeyHash = Crypto.sha256().digest(publicKey);
long id = Convert.fullHashToId(publicKeyHash);
GeneratorStateImpl generator = new GeneratorStateImpl(secretPhrase, nonce, publicKey, id);
GeneratorStateImpl curGen = generators.get(id);
if (curGen == null || generator.getBlock() > curGen.getBlock() || generator.getDeadline().compareTo(curGen.getDeadline()) < 0) {
generators.put(id, generator);
listeners.notify(generator, Event.NONCE_SUBMITTED);
if (logger.isDebugEnabled()) {
logger.debug("Account {} started mining, deadline {} seconds", Convert.toUnsignedLong(id), generator.getDeadline());
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Account {} already has a better nonce", Convert.toUnsignedLong(id));
}
}
return generator;
}
3 GeneratorImpl有一个定时任务,每0.5秒跑一次,使用generator出块
出块调用的方法是generator.getValue().forge(blockchainProcessor);
private Runnable generateBlockThread(BlockchainProcessor blockchainProcessor) {
return () -> {
if (blockchainProcessor.isScanning()) {
return;
}
try {
long currentBlock = blockchain.getLastBlock().getHeight();
Iterator<Entry<Long, GeneratorStateImpl>> it = generators.entrySet().iterator();
while (it.hasNext() && !Thread.currentThread().isInterrupted() && ThreadPool.running.get()) {
Entry<Long, GeneratorStateImpl> generator = it.next();
if (currentBlock < generator.getValue().getBlock()) {
generator.getValue().forge(blockchainProcessor);
} else {
it.remove();
}
}
} catch (BlockchainProcessor.BlockNotAcceptedException e) {
logger.debug("Error in block generation thread", e);
}
};
}
4 forge方法最终调用的是BlockchainProcessor的generateBlock方法,generateBlock会调用pushBlock方法,
pushBlock会验证执行交易,并且把block推到其他peerr
出块和向其他peer推送交易
logger.debug("Successfully pushed {} (height {})", block.getId(), block.getHeight());
statisticsManager.blockAdded();
blockListeners.notify(block, Event.BLOCK_PUSHED);
if (block.getTimestamp() >= timeService.getEpochTime() - MAX_TIMESTAMP_DIFFERENCE) {
Peers.sendToSomePeers(block);
}
BlockchainProcessorImpl中调度多个定时任务
getMoreBlocksThread:获取更多block
blockImporterThread:使用区块,调用verifyGenerationSignature会使用到setPocTime
pocVerificationThread:验证区块->blockService.preVerify->setPocTime
1 GeneratorImpl.calculateHit计算Scoop number