Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 67bbfea87d | |||
| 220c6939b1 | |||
| fd27ea0e8f | |||
| 4bb1982806 | |||
| 9283677a2b | |||
| d4180a5084 | |||
| 2721b16c46 | |||
| f1ff6a21ee | |||
| 530079b8a5 | |||
| 2ca428bbbb | |||
| 8a27a8cce4 | |||
| 7eec4d742c | |||
| 3f66975e1b | |||
| b7f8c973f4 | |||
| 98e51feb46 | |||
| 68fccd2edb | |||
| 72e414b2b7 | |||
| 79a31ce57c | |||
| 488ed8e562 | |||
| b3bce1651b | |||
| e9a2e4b076 | |||
| cc2f031a32 | |||
| d0eea1bf91 | |||
| 53ba29b288 | |||
| c5f3a03d1e | |||
| da9cdf7df3 | |||
| 8a7f1b1b3a | |||
| aa14620a50 | |||
| 1fa6a7c11a | |||
| 25806b67a2 | |||
| 47ff7e228e | |||
| c135787620 | |||
| bbc3d44526 | |||
| 2e747beda6 | |||
| b9a9be2795 | |||
| 19414b061b | |||
| 74b5ab1798 | |||
| 83cda110aa | |||
| 6f87546492 | |||
| 1f7c89a95c | |||
| 0df11d24f5 | |||
| a6c27a1e3f | |||
| 0a5fa12ce5 | |||
| c090b0cf62 | |||
| 45660e3d96 | |||
| 732190bb11 | |||
| b667f88d0e | |||
| 255d312ae0 | |||
| cc92ae7998 | |||
| ae9905e4c7 | |||
| 38469c26ff | |||
| 07b460147e |
@@ -120,7 +120,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {os: ubuntu-18.04}
|
||||
- {os: ubuntu-20.04}
|
||||
- {os: ubuntu-22.04}
|
||||
|
||||
|
||||
+10
-2
@@ -93,12 +93,20 @@ else()
|
||||
include_directories(external/src/curl/include)
|
||||
endif()
|
||||
|
||||
if ((CMAKE_CXX_COMPILER_ID MATCHES MSVC) OR STATIC_BINARY OR STATIC_LIBS)
|
||||
set(UV_INCLUDE_DIR external/src/libuv/include)
|
||||
set(ZMQ_INCLUDE_DIR external/src/libzmq/include)
|
||||
else()
|
||||
find_path(UV_INCLUDE_DIR NAMES uv.h PATH_SUFFIXES "include")
|
||||
find_path(ZMQ_INCLUDE_DIR NAMES zmq.h PATH_SUFFIXES "include")
|
||||
endif()
|
||||
|
||||
include_directories(src)
|
||||
include_directories(external/src)
|
||||
include_directories(external/src/cryptonote)
|
||||
include_directories(external/src/libuv/include)
|
||||
include_directories(${UV_INCLUDE_DIR})
|
||||
include_directories(external/src/cppzmq)
|
||||
include_directories(external/src/libzmq/include)
|
||||
include_directories(${ZMQ_INCLUDE_DIR})
|
||||
if (WITH_RANDOMX)
|
||||
include_directories(external/src/RandomX/src)
|
||||
endif()
|
||||
|
||||
@@ -7,6 +7,8 @@ Pool status and monitoring pages can be found at https://p2pool.io/, https://p2p
|
||||
### Build Status
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 671 B |
@@ -0,0 +1,304 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>P2Pool - Decentralized Monero mining pool</title>
|
||||
<link rel="shortcut icon" type="image/png" href="favicon-32x32.png"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.5">
|
||||
<style>
|
||||
a {
|
||||
color: #e09000;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #808080;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #383838;
|
||||
color: #e0e0e0;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
table {
|
||||
color: #c0c0c0;
|
||||
border-spacing:15px 5px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
table.menu_page {
|
||||
width: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
details > summary {
|
||||
padding: 0.25rem;
|
||||
font-size: 2.5rem;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
details > ul > li > span {
|
||||
color: #e09000;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function elapsedTimeStr(ts1, ts2, short_form) {
|
||||
let s = ts2 - ts1;
|
||||
if (s < 0) s = 0;
|
||||
let m = Math.floor(s / 60) % 60;
|
||||
let h = Math.floor(s / 3600) % 24;
|
||||
let d = Math.floor(s / 86400);
|
||||
if (short_form) {
|
||||
if (s >= 86400) return d + 'd';
|
||||
if (s >= 3600) return h + 'h';
|
||||
return m + 'm';
|
||||
}
|
||||
else {
|
||||
let result = m + 'm';
|
||||
if (s >= 3600) result = h + 'h ' + result;
|
||||
if (s >= 86400) result = d + 'd ' + h + 'h';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function set_effort(el, value) {
|
||||
el.innerHTML = value.toFixed(2) + '%';
|
||||
el.style = (value < 100.0) ? "color:#00C000;" : (value < 200.0) ? "color:#E0E000;" : "color:#FF0000;";
|
||||
}
|
||||
|
||||
async function load_data() {
|
||||
init_menu();
|
||||
window.onresize = init_menu;
|
||||
|
||||
let pool_stats_req = await fetch('api/pool/stats');
|
||||
let pool_blocks_req = await fetch('api/pool/blocks');
|
||||
let network_stats_req = await fetch('api/network/stats');
|
||||
let pool_stats = JSON.parse(await pool_stats_req.text());
|
||||
let pool_blocks = JSON.parse(await pool_blocks_req.text());
|
||||
let network_stats = JSON.parse(await network_stats_req.text());
|
||||
|
||||
let pool_hashrate = pool_stats.pool_statistics.hashRate / 1e6;
|
||||
document.getElementById("pool_hashrate").innerHTML = pool_hashrate.toFixed(3) + ' MH/s';
|
||||
document.getElementById("pool_miners").innerHTML = pool_stats.pool_statistics.miners;
|
||||
|
||||
let ts = Math.floor(Date.now() / 1000);
|
||||
|
||||
let n = pool_stats.pool_statistics.lastBlockFound;
|
||||
document.getElementById("pool_last_block").innerHTML = n ? ('<a href="/explorer/block/' + n + '" target="_blank">' + n + '</a> (' + elapsedTimeStr(pool_stats.pool_statistics.lastBlockFoundTime, ts, 0) + ' ago)') : 'N/A';
|
||||
document.getElementById("pool_total_blocks").innerHTML = pool_stats.pool_statistics.totalBlocksFound;
|
||||
|
||||
let t = document.getElementById('menu_blocks');
|
||||
let cur_effort = (pool_stats.pool_statistics.totalHashes - ((pool_blocks.length > 0) ? pool_blocks[0].totalHashes : 0)) * 100.0 / network_stats.difficulty;
|
||||
set_effort(document.getElementById("current_effort"), (cur_effort >= 0) ? cur_effort : 0);
|
||||
let total_effort = 0.0;
|
||||
let num_blocks_with_effort = 0;
|
||||
for (let i = 0, m = Math.min(pool_blocks.length, 50); i < m; ++i) {
|
||||
if (t.rows.length <= i + 1) t.insertRow(-1);
|
||||
let row = t.rows[i + 1];
|
||||
while (row.cells.length < 3) row.insertCell(-1);
|
||||
|
||||
let height = row.cells[0];
|
||||
let effort = row.cells[1];
|
||||
let age = row.cells[2];
|
||||
|
||||
n = pool_blocks[i].height;
|
||||
height.innerHTML = '<a href="/explorer/block/' + pool_blocks[i].hash + '" target="_blank">' + n + '</a>';
|
||||
|
||||
if ((i + 1 < pool_blocks.length) && (pool_blocks[i + 1].totalHashes > 0)) {
|
||||
let value = (pool_blocks[i].totalHashes - pool_blocks[i + 1].totalHashes) * 100.0 / pool_blocks[i].difficulty;
|
||||
total_effort += value;
|
||||
++num_blocks_with_effort;
|
||||
set_effort(effort, value);
|
||||
}
|
||||
else {
|
||||
effort.innerHTML = "N/A";
|
||||
}
|
||||
|
||||
age.innerHTML = elapsedTimeStr(pool_blocks[i].ts, ts, 1);
|
||||
}
|
||||
if (num_blocks_with_effort > 0) {
|
||||
let average_effort = total_effort / num_blocks_with_effort;
|
||||
set_effort(document.getElementById("average_effort"), average_effort);
|
||||
}
|
||||
|
||||
let network_hashrate = network_stats.difficulty / 120e6;
|
||||
document.getElementById("monero_hashrate").innerHTML = network_hashrate.toFixed(3) + ' MH/s';
|
||||
document.getElementById("monero_last_block").innerHTML = network_stats.height + ' (' + elapsedTimeStr(network_stats.timestamp, ts, 0) + ' ago)';
|
||||
}
|
||||
|
||||
const menu = ['pool', 'blocks', 'help', 'faq'];
|
||||
|
||||
function init_menu()
|
||||
{
|
||||
let is_mobile = false;
|
||||
|
||||
if (navigator.userAgentData) {
|
||||
is_mobile = navigator.userAgentData.mobile;
|
||||
}
|
||||
else {
|
||||
const mobile_preg = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i;
|
||||
is_mobile = mobile_preg.test(navigator.userAgent) || mobile_preg.test(navigator.platform);
|
||||
}
|
||||
|
||||
let e = document.getElementById('main_menu');
|
||||
|
||||
if (!is_mobile) {
|
||||
e.style.cursor = "pointer";
|
||||
document.querySelectorAll("details > summary").forEach(el => { el.style.cursor = "pointer"; });
|
||||
}
|
||||
|
||||
e.style.width = (window.innerWidth > 1600) ? "1600px" : "100%";
|
||||
|
||||
menu.forEach(element => {
|
||||
let e2 = document.getElementById('menu_' + element);
|
||||
e2.style.width = e.style.width;
|
||||
});
|
||||
}
|
||||
|
||||
function toggle_menu(name) {
|
||||
if (!name) {
|
||||
name = 'pool';
|
||||
}
|
||||
|
||||
menu.forEach(element => {
|
||||
let e = document.getElementById('menu_' + element);
|
||||
let b = document.getElementById('button_' + element);
|
||||
if (element == name) {
|
||||
e.style.display = 'block';
|
||||
b.style.backgroundColor = '#646464';
|
||||
window.location.hash = '#' + name;
|
||||
}
|
||||
else {
|
||||
e.style.display = 'none';
|
||||
b.style.backgroundColor = '#404040';
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="load_data();setInterval(load_data, 2000);toggle_menu(window.location.hash.substr(1));">
|
||||
<div style="font-size:3rem;text-align:center;">P2Pool</div><div style="font-size:1.1rem;text-align:center;">Decentralized Monero mining pool</div>
|
||||
<br>
|
||||
<table id="main_menu" style="width:100%;margin-left:auto;margin-right:auto;table-layout:fixed;text-align:center;-ms-touch-action:none;touch-action:none;"><tr>
|
||||
<td onclick="toggle_menu('pool')" id="button_pool">Pool</td>
|
||||
<td onclick="toggle_menu('blocks')" id="button_blocks">Blocks</td>
|
||||
<td onclick="toggle_menu('help')" id="button_help">Get started</td>
|
||||
<td onclick="toggle_menu('faq')" id="button_faq">FAQ</td>
|
||||
</tr></table>
|
||||
<br>
|
||||
<table id="menu_pool" class="menu_page">
|
||||
<tr><td></td><td style="width:17rem;">Pool</td><td>Network</td></tr>
|
||||
<tr><td style="padding-bottom: 1rem;"></td><td></td><td></td></tr>
|
||||
<tr><td>Hashrate</td><td><span id="pool_hashrate">N/A</span></td><td><span id="monero_hashrate">N/A</span></td></tr>
|
||||
<tr><td>Miners</td><td><span id="pool_miners">N/A</span></td><td></td></tr>
|
||||
<tr><td>Last block</td><td><span id="pool_last_block">N/A</span></td><td><span id="monero_last_block">N/A</span></td></tr>
|
||||
<tr><td>Total blocks</td><td><span id="pool_total_blocks">N/A</span></td><td></td></tr>
|
||||
<tr><td>Current effort</td><td><span id="current_effort">N/A</span></td><td></td></tr>
|
||||
<tr><td>Average effort<br><div style="text-align:center;font-size:0.9rem">last 50 blocks</div></td><td><span id="average_effort">N/A</span></td><td></td></tr>
|
||||
<tr><td> </td><td></td><td></td></tr>
|
||||
<tr><td>Fee</td><td>0%</td><td></td></tr>
|
||||
<tr><td>Min payout</td><td>0.0003 XMR</td><td></td></tr>
|
||||
<tr><td>Payout scheme</td><td><a href="https://en.wikipedia.org/wiki/Mining_pool#Pay-per-last-N-shares" target="_blank">PPLNS</a></td><td></td></tr>
|
||||
<tr><td> </td><td></td><td></td></tr>
|
||||
<tr><td>Statistics</td><td><a href="https://p2pool.observer/" target="_blank">P2Pool observer</a></td><td></td></tr>
|
||||
</table>
|
||||
<table id="menu_blocks" class="menu_page">
|
||||
<tr><td style="padding-right:5rem;">Height</td><td style="padding-right:5rem;">Effort</td><td>Age</td></tr>
|
||||
</table>
|
||||
<table id="menu_help" class="menu_page"><tr><td>
|
||||
|
||||
<details><summary>P2Pool video guide</summary>
|
||||
<ul>
|
||||
<li>A detailed <a href="https://www.youtube.com/watch?v=NbxbRu-2GWI" target="_blank">video guide</a> to install P2Pool and start mining
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<details><summary>Setup Monero wallet</summary>
|
||||
Wallets with confirmed P2Pool support<br>
|
||||
<span style="font-size:0.9rem;color:#e09000;">You might not see P2Pool payouts in other wallets if they're not updated!</span>
|
||||
<ul>
|
||||
<li>Official <a href="https://www.getmonero.org/downloads/" target="_blank">Monero CLI and GUI v0.18.1.0</a> and newer
|
||||
<li><a href="https://play.google.com/store/apps/details?id=com.m2049r.xmrwallet" target="_blank">Monerujo v3.0.2 "Fluorine Fermi"</a> and newer
|
||||
<li><a href="https://play.google.com/store/apps/details?id=com.cakewallet.cake_wallet" target="_blank">Cake Wallet v4.4.5</a> and newer
|
||||
<li><a href="https://featherwallet.org/" target="_blank">Feather Wallet v2.1.0</a> and newer
|
||||
<li><a href="https://mymonero.com/" target="_blank">MyMonero</a>
|
||||
</ul>
|
||||
It's highly recommended to create a new wallet for mining because wallet addresses are public on p2pool. You have to use the primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
|
||||
</details>
|
||||
|
||||
<details><summary>Setup Monero node</summary>
|
||||
<ul>
|
||||
<li>Download the latest Monero <a href="https://www.getmonero.org/downloads/" target="_blank">release</a>
|
||||
<li>[Optional] Open port <span>18080</span> (Monero p2p port) in your firewall to ensure better connectivity
|
||||
<li>Run <span>./monerod --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist</span>
|
||||
<li>Wait until it's fully synchronized. If you didn't run Monero node before, it can take up to several days to synchronize (5-6 hours on a modern PC with fast SSD and fast Internet connection). You can add <span>--prune-blockchain</span> argument to the command line to run a pruned node (3-4 times less disk usage)
|
||||
</ul></details>
|
||||
|
||||
<details><summary>Setup P2Pool node</summary>
|
||||
<ul>
|
||||
<li>Download the latest P2Pool <a href="https://github.com/SChernykh/p2pool/releases/latest" target="_blank">release</a>
|
||||
<li>[Optional] Open port <span>37889</span> (P2Pool port) or <span>37888</span> (P2Pool mini port) in your firewall to ensure better connectivity
|
||||
<li>Run <span>./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS</span>
|
||||
<li>It should take no more than 5-10 minutes to synchronize
|
||||
</ul></details>
|
||||
|
||||
<details><summary>Miner software</summary>
|
||||
<ul>
|
||||
<li><a href="https://xmrig.com/" target="_blank">XMRig</a> is recommended. <a href="https://xmrig.com/proxy" target="_blank">XMRig Proxy</a> is fully supported.
|
||||
<li>Once P2Pool sync is complete, XMRig should be able to connect to the stratum server on port <span>3333</span>
|
||||
<li>Run <span>./xmrig -o 127.0.0.1:3333</span>. Note that you don't need to specify wallet address for xmrig. Wallet address set in xmrig config will be ignored!
|
||||
<li>To set custom fixed difficulty for your miner (for example, <span>10000</span>), run <span>./xmrig -u x+10000 -o 127.0.0.1:3333</span>
|
||||
</ul></details>
|
||||
|
||||
<details><summary>Checking your progress</summary>
|
||||
<ul>
|
||||
<li>You can enter your wallet address at <a href="https://p2pool.observer/" target="_blank">P2Pool Observer</a>
|
||||
<li>P2Pool Observer for P2Pool mini can be found <a href="https://mini.p2pool.observer/" target="_blank">here</a>
|
||||
<li>You will find your wallet's statistics there only after you've mined at least 1 P2Pool share
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<details><summary>Help and support</summary>
|
||||
P2Pool is a decentralized community, there is no dedicated support. You can reach and get help from other miners on:
|
||||
<ul>
|
||||
<li>IRC channels: <span>#monero-pools</span> and <span>#p2pool-log</span> (<span>#p2pool-mini</span> for P2Pool mini) on <a href="https://libera.chat/" target="_blank">Libera.Chat</a>
|
||||
<li>Reddit: <a href="https://reddit.com/r/MoneroMining/" target="_blank">r/MoneroMining</a>
|
||||
</ul></details>
|
||||
</td></tr></table>
|
||||
<table id="menu_faq" class="menu_page"><tr><td>
|
||||
<ul>
|
||||
<li><span style="font-size:2rem;color:#e09000;">What are system requirements to run P2Pool?</span>
|
||||
<ul><li>P2Pool needs 0.5 GB disc space and 2.6 GB RAM to run (0.6 GB RAM in light mode). Add this to monerod requirements if you run both on the same machine. Add <span style="color:#808080;">--light-mode</span> to p2pool command line to reduce the memory usage to 0.6 GB, or <span style="color:#808080;">--no-randomx --no-cache</span> to reduce the memory usage to less than 100 MB.</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool work with XMRig Proxy?</span>
|
||||
<ul><li>Yes, P2Pool fully supports <a href="https://xmrig.com/proxy" target="_blank">XMRig Proxy</a>.</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">Does monerod have to be run on the same machine as P2Pool?</span>
|
||||
<ul><li>No, you can run them on different machines and even connect multiple p2pool instances to a single server running monerod</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool work with pruned Monero node?</span>
|
||||
<ul><li>Yes, you can use pruned Monero node to mine on P2Pool</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool require incoming connections to work properly?</span>
|
||||
<ul><li>P2Pool (and Monero) can work with only outgoing connections, but it's recommended to open ports 18080 (Monero) and 37889/37888 (P2Pool/P2Pool mini) for better connectivity with the rest of the network.</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">I have been mining for several hours and still got no rewards?</span>
|
||||
<ul><li>P2Pool doesn't work exactly like regular pools - there's no "pending balance" here. You need to find a pool share first and then wait for P2Pool to find a Monero block. You'll get a payout to your XMR wallet as soon as a block is found and you have shares in PPLNS window. It can take several days to a week to find a share if your hashrate is low. Type "status" command in P2Pool console or visit <a href="https://p2pool.observer/" target="_blank">P2Pool observer</a> to check your progress.</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">I am mining but I can't find any shares</span>
|
||||
<ul><li>First check for any warnings and errors in P2Pool console and logs. If everything works fine, you can check the <a href="https://p2pool.observer/calculate-share-time" target="_blank">average share time calculator</a> to get an idea how often you should find a share with your hashrate. If this time is more than 1 day, it's recommended to switch to P2Pool mini by adding <span style="color:#808080;">--mini</span> to P2Pool command line.</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">I get an error "monerod RPC ... is incompatible, update to RPC >= v3.8"</span>
|
||||
<ul><li>Use monerod v0.18.0.0 or newer</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">I get warnings "peer ... is ahead on mainchain (height ..., your height ...). Is your monerod stuck or lagging?"</span>
|
||||
<ul><li>Make sure you add these arguments to monerod command line: <span style="color:#808080;">--zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist</span></ul><br>
|
||||
</ul>
|
||||
</td></tr></table>
|
||||
<br><br>
|
||||
<div style="position:fixed;width:100%;bottom:0;background-color:#383838;font-size:1rem;text-align:center;color:#b0b0b0;">
|
||||
<a href="api/" target="_blank">Pool API</a> | <a href="https://github.com/SChernykh/p2pool" target="_blank">Source</a> | <a href="http://yucmgsbw7nknw7oi3bkuwudvc657g2xcqahhbjyewazusyytapqo4xid.onion/" target="_blank">Onion URL</a><br>
|
||||
Donate to support the project:<br><a href="monero:44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg?tx_description=Donation%20to%20P2Pool" style="font-size:0.9rem;color:#b0b0b0;">44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg</a><br>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 671 B |
@@ -0,0 +1,304 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>P2Pool mini - Decentralized Monero mining pool</title>
|
||||
<link rel="shortcut icon" type="image/png" href="favicon-32x32.png"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.5">
|
||||
<style>
|
||||
a {
|
||||
color: #e09000;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: #808080;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #383838;
|
||||
color: #e0e0e0;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
table {
|
||||
color: #c0c0c0;
|
||||
border-spacing:15px 5px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
table.menu_page {
|
||||
width: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
details > summary {
|
||||
padding: 0.25rem;
|
||||
font-size: 2.5rem;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
details > ul > li > span {
|
||||
color: #e09000;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function elapsedTimeStr(ts1, ts2, short_form) {
|
||||
let s = ts2 - ts1;
|
||||
if (s < 0) s = 0;
|
||||
let m = Math.floor(s / 60) % 60;
|
||||
let h = Math.floor(s / 3600) % 24;
|
||||
let d = Math.floor(s / 86400);
|
||||
if (short_form) {
|
||||
if (s >= 86400) return d + 'd';
|
||||
if (s >= 3600) return h + 'h';
|
||||
return m + 'm';
|
||||
}
|
||||
else {
|
||||
let result = m + 'm';
|
||||
if (s >= 3600) result = h + 'h ' + result;
|
||||
if (s >= 86400) result = d + 'd ' + h + 'h';
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function set_effort(el, value) {
|
||||
el.innerHTML = value.toFixed(2) + '%';
|
||||
el.style = (value < 100.0) ? "color:#00C000;" : (value < 200.0) ? "color:#E0E000;" : "color:#FF0000;";
|
||||
}
|
||||
|
||||
async function load_data() {
|
||||
init_menu();
|
||||
window.onresize = init_menu;
|
||||
|
||||
let pool_stats_req = await fetch('api/pool/stats');
|
||||
let pool_blocks_req = await fetch('api/pool/blocks');
|
||||
let network_stats_req = await fetch('api/network/stats');
|
||||
let pool_stats = JSON.parse(await pool_stats_req.text());
|
||||
let pool_blocks = JSON.parse(await pool_blocks_req.text());
|
||||
let network_stats = JSON.parse(await network_stats_req.text());
|
||||
|
||||
let pool_hashrate = pool_stats.pool_statistics.hashRate / 1e6;
|
||||
document.getElementById("pool_hashrate").innerHTML = pool_hashrate.toFixed(3) + ' MH/s';
|
||||
document.getElementById("pool_miners").innerHTML = pool_stats.pool_statistics.miners;
|
||||
|
||||
let ts = Math.floor(Date.now() / 1000);
|
||||
|
||||
let n = pool_stats.pool_statistics.lastBlockFound;
|
||||
document.getElementById("pool_last_block").innerHTML = n ? ('<a href="/explorer/block/' + n + '" target="_blank">' + n + '</a> (' + elapsedTimeStr(pool_stats.pool_statistics.lastBlockFoundTime, ts, 0) + ' ago)') : 'N/A';
|
||||
document.getElementById("pool_total_blocks").innerHTML = pool_stats.pool_statistics.totalBlocksFound;
|
||||
|
||||
let t = document.getElementById('menu_blocks');
|
||||
let cur_effort = (pool_stats.pool_statistics.totalHashes - ((pool_blocks.length > 0) ? pool_blocks[0].totalHashes : 0)) * 100.0 / network_stats.difficulty;
|
||||
set_effort(document.getElementById("current_effort"), (cur_effort >= 0) ? cur_effort : 0);
|
||||
let total_effort = 0.0;
|
||||
let num_blocks_with_effort = 0;
|
||||
for (let i = 0, m = Math.min(pool_blocks.length, 50); i < m; ++i) {
|
||||
if (t.rows.length <= i + 1) t.insertRow(-1);
|
||||
let row = t.rows[i + 1];
|
||||
while (row.cells.length < 3) row.insertCell(-1);
|
||||
|
||||
let height = row.cells[0];
|
||||
let effort = row.cells[1];
|
||||
let age = row.cells[2];
|
||||
|
||||
n = pool_blocks[i].height;
|
||||
height.innerHTML = '<a href="/explorer/block/' + pool_blocks[i].hash + '" target="_blank">' + n + '</a>';
|
||||
|
||||
if ((i + 1 < pool_blocks.length) && (pool_blocks[i + 1].totalHashes > 0)) {
|
||||
let value = (pool_blocks[i].totalHashes - pool_blocks[i + 1].totalHashes) * 100.0 / pool_blocks[i].difficulty;
|
||||
total_effort += value;
|
||||
++num_blocks_with_effort;
|
||||
set_effort(effort, value);
|
||||
}
|
||||
else {
|
||||
effort.innerHTML = "N/A";
|
||||
}
|
||||
|
||||
age.innerHTML = elapsedTimeStr(pool_blocks[i].ts, ts, 1);
|
||||
}
|
||||
if (num_blocks_with_effort > 0) {
|
||||
let average_effort = total_effort / num_blocks_with_effort;
|
||||
set_effort(document.getElementById("average_effort"), average_effort);
|
||||
}
|
||||
|
||||
let network_hashrate = network_stats.difficulty / 120e6;
|
||||
document.getElementById("monero_hashrate").innerHTML = network_hashrate.toFixed(3) + ' MH/s';
|
||||
document.getElementById("monero_last_block").innerHTML = network_stats.height + ' (' + elapsedTimeStr(network_stats.timestamp, ts, 0) + ' ago)';
|
||||
}
|
||||
|
||||
const menu = ['pool', 'blocks', 'help', 'faq'];
|
||||
|
||||
function init_menu()
|
||||
{
|
||||
let is_mobile = false;
|
||||
|
||||
if (navigator.userAgentData) {
|
||||
is_mobile = navigator.userAgentData.mobile;
|
||||
}
|
||||
else {
|
||||
const mobile_preg = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i;
|
||||
is_mobile = mobile_preg.test(navigator.userAgent) || mobile_preg.test(navigator.platform);
|
||||
}
|
||||
|
||||
let e = document.getElementById('main_menu');
|
||||
|
||||
if (!is_mobile) {
|
||||
e.style.cursor = "pointer";
|
||||
document.querySelectorAll("details > summary").forEach(el => { el.style.cursor = "pointer"; });
|
||||
}
|
||||
|
||||
e.style.width = (window.innerWidth > 1600) ? "1600px" : "100%";
|
||||
|
||||
menu.forEach(element => {
|
||||
let e2 = document.getElementById('menu_' + element);
|
||||
e2.style.width = e.style.width;
|
||||
});
|
||||
}
|
||||
|
||||
function toggle_menu(name) {
|
||||
if (!name) {
|
||||
name = 'pool';
|
||||
}
|
||||
|
||||
menu.forEach(element => {
|
||||
let e = document.getElementById('menu_' + element);
|
||||
let b = document.getElementById('button_' + element);
|
||||
if (element == name) {
|
||||
e.style.display = 'block';
|
||||
b.style.backgroundColor = '#646464';
|
||||
window.location.hash = '#' + name;
|
||||
}
|
||||
else {
|
||||
e.style.display = 'none';
|
||||
b.style.backgroundColor = '#404040';
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="load_data();setInterval(load_data, 2000);toggle_menu(window.location.hash.substr(1));">
|
||||
<div style="font-size:3rem;text-align:center;">P2Pool mini</div><div style="font-size:1.1rem;text-align:center;">Decentralized Monero mining pool</div>
|
||||
<br>
|
||||
<table id="main_menu" style="width:100%;margin-left:auto;margin-right:auto;table-layout:fixed;text-align:center;-ms-touch-action:none;touch-action:none;"><tr>
|
||||
<td onclick="toggle_menu('pool')" id="button_pool">Pool</td>
|
||||
<td onclick="toggle_menu('blocks')" id="button_blocks">Blocks</td>
|
||||
<td onclick="toggle_menu('help')" id="button_help">Get started</td>
|
||||
<td onclick="toggle_menu('faq')" id="button_faq">FAQ</td>
|
||||
</tr></table>
|
||||
<br>
|
||||
<table id="menu_pool" class="menu_page">
|
||||
<tr><td></td><td style="width:17rem;">Pool</td><td>Network</td></tr>
|
||||
<tr><td style="padding-bottom: 1rem;"></td><td></td><td></td></tr>
|
||||
<tr><td>Hashrate</td><td><span id="pool_hashrate">N/A</span></td><td><span id="monero_hashrate">N/A</span></td></tr>
|
||||
<tr><td>Miners</td><td><span id="pool_miners">N/A</span></td><td></td></tr>
|
||||
<tr><td>Last block</td><td><span id="pool_last_block">N/A</span></td><td><span id="monero_last_block">N/A</span></td></tr>
|
||||
<tr><td>Total blocks</td><td><span id="pool_total_blocks">N/A</span></td><td></td></tr>
|
||||
<tr><td>Current effort</td><td><span id="current_effort">N/A</span></td><td></td></tr>
|
||||
<tr><td>Average effort<br><div style="text-align:center;font-size:0.9rem">last 50 blocks</div></td><td><span id="average_effort">N/A</span></td><td></td></tr>
|
||||
<tr><td> </td><td></td><td></td></tr>
|
||||
<tr><td>Fee</td><td>0%</td><td></td></tr>
|
||||
<tr><td>Min payout</td><td>0.0003 XMR</td><td></td></tr>
|
||||
<tr><td>Payout scheme</td><td><a href="https://en.wikipedia.org/wiki/Mining_pool#Pay-per-last-N-shares" target="_blank">PPLNS</a></td><td></td></tr>
|
||||
<tr><td> </td><td></td><td></td></tr>
|
||||
<tr><td>Statistics</td><td><a href="https://mini.p2pool.observer/" target="_blank">P2Pool observer</a></td><td></td></tr>
|
||||
</table>
|
||||
<table id="menu_blocks" class="menu_page">
|
||||
<tr><td style="padding-right:5rem;">Height</td><td style="padding-right:5rem;">Effort</td><td>Age</td></tr>
|
||||
</table>
|
||||
<table id="menu_help" class="menu_page"><tr><td>
|
||||
|
||||
<details><summary>P2Pool video guide</summary>
|
||||
<ul>
|
||||
<li>A detailed <a href="https://www.youtube.com/watch?v=NbxbRu-2GWI" target="_blank">video guide</a> to install P2Pool and start mining
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<details><summary>Setup Monero wallet</summary>
|
||||
Wallets with confirmed P2Pool support<br>
|
||||
<span style="font-size:0.9rem;color:#e09000;">You might not see P2Pool payouts in other wallets if they're not updated!</span>
|
||||
<ul>
|
||||
<li>Official <a href="https://www.getmonero.org/downloads/" target="_blank">Monero CLI and GUI v0.18.1.0</a> and newer
|
||||
<li><a href="https://play.google.com/store/apps/details?id=com.m2049r.xmrwallet" target="_blank">Monerujo v3.0.2 "Fluorine Fermi"</a> and newer
|
||||
<li><a href="https://play.google.com/store/apps/details?id=com.cakewallet.cake_wallet" target="_blank">Cake Wallet v4.4.5</a> and newer
|
||||
<li><a href="https://featherwallet.org/" target="_blank">Feather Wallet v2.1.0</a> and newer
|
||||
<li><a href="https://mymonero.com/" target="_blank">MyMonero</a>
|
||||
</ul>
|
||||
It's highly recommended to create a new wallet for mining because wallet addresses are public on p2pool. You have to use the primary wallet address for mining. Subaddresses and integrated addresses are not supported, just like with monerod solo mining.
|
||||
</details>
|
||||
|
||||
<details><summary>Setup Monero node</summary>
|
||||
<ul>
|
||||
<li>Download the latest Monero <a href="https://www.getmonero.org/downloads/" target="_blank">release</a>
|
||||
<li>[Optional] Open port <span>18080</span> (Monero p2p port) in your firewall to ensure better connectivity
|
||||
<li>Run <span>./monerod --zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist</span>
|
||||
<li>Wait until it's fully synchronized. If you didn't run Monero node before, it can take up to several days to synchronize (5-6 hours on a modern PC with fast SSD and fast Internet connection). You can add <span>--prune-blockchain</span> argument to the command line to run a pruned node (3-4 times less disk usage)
|
||||
</ul></details>
|
||||
|
||||
<details><summary>Setup P2Pool node</summary>
|
||||
<ul>
|
||||
<li>Download the latest P2Pool <a href="https://github.com/SChernykh/p2pool/releases/latest" target="_blank">release</a>
|
||||
<li>[Optional] Open port <span>37889</span> (P2Pool port) or <span>37888</span> (P2Pool mini port) in your firewall to ensure better connectivity
|
||||
<li>Run <span>./p2pool --host 127.0.0.1 --wallet YOUR_WALLET_ADDRESS --mini</span>
|
||||
<li>It should take no more than 5-10 minutes to synchronize
|
||||
</ul></details>
|
||||
|
||||
<details><summary>Miner software</summary>
|
||||
<ul>
|
||||
<li><a href="https://xmrig.com/" target="_blank">XMRig</a> is recommended. <a href="https://xmrig.com/proxy" target="_blank">XMRig Proxy</a> is fully supported.
|
||||
<li>Once P2Pool sync is complete, XMRig should be able to connect to the stratum server on port <span>3333</span>
|
||||
<li>Run <span>./xmrig -o 127.0.0.1:3333</span>. Note that you don't need to specify wallet address for xmrig. Wallet address set in xmrig config will be ignored!
|
||||
<li>To set custom fixed difficulty for your miner (for example, <span>10000</span>), run <span>./xmrig -u x+10000 -o 127.0.0.1:3333</span>
|
||||
</ul></details>
|
||||
|
||||
<details><summary>Checking your progress</summary>
|
||||
<ul>
|
||||
<li>You can enter your wallet address at <a href="https://p2pool.observer/" target="_blank">P2Pool Observer</a>
|
||||
<li>P2Pool Observer for P2Pool mini can be found <a href="https://mini.p2pool.observer/" target="_blank">here</a>
|
||||
<li>You will find your wallet's statistics there only after you've mined at least 1 P2Pool share
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<details><summary>Help and support</summary>
|
||||
P2Pool is a decentralized community, there is no dedicated support. You can reach and get help from other miners on:
|
||||
<ul>
|
||||
<li>IRC channels: <span>#monero-pools</span> and <span>#p2pool-log</span> (<span>#p2pool-mini</span> for P2Pool mini) on <a href="https://libera.chat/" target="_blank">Libera.Chat</a>
|
||||
<li>Reddit: <a href="https://reddit.com/r/MoneroMining/" target="_blank">r/MoneroMining</a>
|
||||
</ul></details>
|
||||
</td></tr></table>
|
||||
<table id="menu_faq" class="menu_page"><tr><td>
|
||||
<ul>
|
||||
<li><span style="font-size:2rem;color:#e09000;">What are system requirements to run P2Pool?</span>
|
||||
<ul><li>P2Pool needs 0.5 GB disc space and 2.6 GB RAM to run (0.6 GB RAM in light mode). Add this to monerod requirements if you run both on the same machine. Add <span style="color:#808080;">--light-mode</span> to p2pool command line to reduce the memory usage to 0.6 GB, or <span style="color:#808080;">--no-randomx --no-cache</span> to reduce the memory usage to less than 100 MB.</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool work with XMRig Proxy?</span>
|
||||
<ul><li>Yes, P2Pool fully supports <a href="https://xmrig.com/proxy" target="_blank">XMRig Proxy</a>.</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">Does monerod have to be run on the same machine as P2Pool?</span>
|
||||
<ul><li>No, you can run them on different machines and even connect multiple p2pool instances to a single server running monerod</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool work with pruned Monero node?</span>
|
||||
<ul><li>Yes, you can use pruned Monero node to mine on P2Pool</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">Does P2Pool require incoming connections to work properly?</span>
|
||||
<ul><li>P2Pool (and Monero) can work with only outgoing connections, but it's recommended to open ports 18080 (Monero) and 37889/37888 (P2Pool/P2Pool mini) for better connectivity with the rest of the network.</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">I have been mining for several hours and still got no rewards?</span>
|
||||
<ul><li>P2Pool doesn't work exactly like regular pools - there's no "pending balance" here. You need to find a pool share first and then wait for P2Pool to find a Monero block. You'll get a payout to your XMR wallet as soon as a block is found and you have shares in PPLNS window. It can take several days to a week to find a share if your hashrate is low. Type "status" command in P2Pool console or visit <a href="https://mini.p2pool.observer/" target="_blank">P2Pool observer</a> to check your progress.</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">I am mining but I can't find any shares</span>
|
||||
<ul><li>First check for any warnings and errors in P2Pool console and logs. If everything works fine, you can check the <a href="https://p2pool.observer/calculate-share-time" target="_blank">average share time calculator</a> to get an idea how often you should find a share with your hashrate. If this time is more than 1 day, it's recommended to switch to P2Pool mini by adding <span style="color:#808080;">--mini</span> to P2Pool command line.</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">I get an error "monerod RPC ... is incompatible, update to RPC >= v3.8"</span>
|
||||
<ul><li>Use monerod v0.18.0.0 or newer</ul><br>
|
||||
<li><span style="font-size:2rem;color:#e09000;">I get warnings "peer ... is ahead on mainchain (height ..., your height ...). Is your monerod stuck or lagging?"</span>
|
||||
<ul><li>Make sure you add these arguments to monerod command line: <span style="color:#808080;">--zmq-pub tcp://127.0.0.1:18083 --disable-dns-checkpoints --enable-dns-blocklist</span></ul><br>
|
||||
</ul>
|
||||
</td></tr></table>
|
||||
<br><br>
|
||||
<div style="position:fixed;width:100%;bottom:0;background-color:#383838;font-size:1rem;text-align:center;color:#b0b0b0;">
|
||||
<a href="api/" target="_blank">Pool API</a> | <a href="https://github.com/SChernykh/p2pool" target="_blank">Source</a> | <a href="http://yucmgsbw7nknw7oi3bkuwudvc657g2xcqahhbjyewazusyytapqo4xid.onion/mini/" target="_blank">Onion URL</a><br>
|
||||
Donate to support the project:<br><a href="monero:44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg?tx_description=Donation%20to%20P2Pool" style="font-size:0.9rem;color:#b0b0b0;">44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg</a><br>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Vendored
+1
-1
Submodule external/src/cppzmq updated: 381f699d37...c66fc6094b
Vendored
+1
-1
Submodule external/src/curl updated: 93d092867f...cd95ee9f77
Vendored
+1
-1
Submodule external/src/libzmq updated: ec6f3b1dba...7fad9599ab
Vendored
+1
-1
Submodule external/src/robin-hood-hashing updated: 7f87d77122...4213dd109f
+1
-1
@@ -201,7 +201,7 @@ void BlockCache::load_all(SideChain& side_chain, P2PServer& server)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (block.deserialize(data + sizeof(uint32_t), n, side_chain, uv_default_loop_checked()) == 0) {
|
||||
if (block.deserialize(data + sizeof(uint32_t), n, side_chain, uv_default_loop_checked(), false) == 0) {
|
||||
server.add_cached_block(block);
|
||||
++blocks_loaded;
|
||||
}
|
||||
|
||||
+123
-86
@@ -34,11 +34,16 @@
|
||||
|
||||
static constexpr char log_category_prefix[] = "BlockTemplate ";
|
||||
|
||||
// Max P2P message size (128 KB) minus BLOCK_RESPONSE header (5 bytes)
|
||||
static constexpr size_t MAX_BLOCK_TEMPLATE_SIZE = 128 * 1024 - (1 + sizeof(uint32_t));
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
BlockTemplate::BlockTemplate(p2pool* pool)
|
||||
: m_pool(pool)
|
||||
BlockTemplate::BlockTemplate(SideChain* sidechain, RandomX_Hasher_Base* hasher)
|
||||
: m_sidechain(sidechain)
|
||||
, m_hasher(hasher)
|
||||
, m_templateId(0)
|
||||
, m_lastUpdated(seconds_since_epoch())
|
||||
, m_blockHeaderSize(0)
|
||||
, m_minerTxOffsetInTemplate(0)
|
||||
, m_minerTxSize(0)
|
||||
@@ -70,7 +75,8 @@ BlockTemplate::BlockTemplate(p2pool* pool)
|
||||
m_merkleTreeMainBranch.reserve(HASH_SIZE * 10);
|
||||
m_mempoolTxs.reserve(1024);
|
||||
m_mempoolTxsOrder.reserve(1024);
|
||||
m_shares.reserve(m_pool->side_chain().chain_window_size() * 2);
|
||||
m_mempoolTxsOrder2.reserve(1024);
|
||||
m_shares.reserve(m_sidechain->chain_window_size() * 2);
|
||||
|
||||
for (size_t i = 0; i < array_size(&BlockTemplate::m_oldTemplates); ++i) {
|
||||
m_oldTemplates[i] = new BlockTemplate(*this);
|
||||
@@ -108,8 +114,10 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
|
||||
|
||||
WriteLock lock(m_lock);
|
||||
|
||||
m_pool = b.m_pool;
|
||||
m_sidechain = b.m_sidechain;
|
||||
m_hasher = b.m_hasher;
|
||||
m_templateId = b.m_templateId;
|
||||
m_lastUpdated = b.m_lastUpdated.load();
|
||||
m_blockTemplateBlob = b.m_blockTemplateBlob;
|
||||
m_merkleTreeMainBranch = b.m_merkleTreeMainBranch;
|
||||
m_blockHeaderSize = b.m_blockHeaderSize;
|
||||
@@ -135,6 +143,7 @@ BlockTemplate& BlockTemplate::operator=(const BlockTemplate& b)
|
||||
m_rewards.clear();
|
||||
m_mempoolTxs.clear();
|
||||
m_mempoolTxsOrder.clear();
|
||||
m_mempoolTxsOrder2.clear();
|
||||
m_shares.clear();
|
||||
|
||||
m_rng = b.m_rng;
|
||||
@@ -203,6 +212,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
}
|
||||
|
||||
++m_templateId;
|
||||
m_lastUpdated = seconds_since_epoch();
|
||||
|
||||
// When block template generation fails for any reason
|
||||
auto use_old_template = [this]() {
|
||||
@@ -217,54 +227,6 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
m_difficulty = data.difficulty;
|
||||
m_seedHash = data.seed_hash;
|
||||
|
||||
// Only choose transactions that were received 10 or more seconds ago
|
||||
size_t total_mempool_transactions;
|
||||
{
|
||||
m_mempoolTxs.clear();
|
||||
|
||||
ReadLock mempool_lock(mempool.m_lock);
|
||||
|
||||
total_mempool_transactions = mempool.m_transactions.size();
|
||||
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
|
||||
for (auto& it : mempool.m_transactions) {
|
||||
if (cur_time >= it.second.time_received + 10) {
|
||||
m_mempoolTxs.emplace_back(it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Safeguard for busy mempool moments
|
||||
// If the block template gets too big, nodes won't be able to send and receive it because of p2p packet size limit
|
||||
// Select 1000 transactions with the highest fee per byte
|
||||
if (m_mempoolTxs.size() > 1000) {
|
||||
std::nth_element(m_mempoolTxs.begin(), m_mempoolTxs.begin() + 1000, m_mempoolTxs.end(),
|
||||
[](const TxMempoolData& tx_a, const TxMempoolData& tx_b)
|
||||
{
|
||||
return tx_a.fee * tx_b.weight > tx_b.fee * tx_a.weight;
|
||||
});
|
||||
m_mempoolTxs.resize(1000);
|
||||
}
|
||||
|
||||
LOGINFO(4, "mempool has " << total_mempool_transactions << " transactions, taking " << m_mempoolTxs.size() << " transactions from it");
|
||||
|
||||
const uint64_t base_reward = get_base_reward(data.already_generated_coins);
|
||||
|
||||
uint64_t total_tx_fees = 0;
|
||||
uint64_t total_tx_weight = 0;
|
||||
for (const TxMempoolData& tx : m_mempoolTxs) {
|
||||
total_tx_fees += tx.fee;
|
||||
total_tx_weight += tx.weight;
|
||||
}
|
||||
|
||||
const uint64_t max_reward = base_reward + total_tx_fees;
|
||||
|
||||
LOGINFO(3, "base reward = " << log::Gray() << log::XMRAmount(base_reward) << log::NoColor() <<
|
||||
", " << log::Gray() << m_mempoolTxs.size() << log::NoColor() <<
|
||||
" transactions, fees = " << log::Gray() << log::XMRAmount(total_tx_fees) << log::NoColor() <<
|
||||
", weight = " << log::Gray() << total_tx_weight);
|
||||
|
||||
m_blockHeader.clear();
|
||||
m_poolBlockTemplate->m_verified = false;
|
||||
|
||||
@@ -296,7 +258,71 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
|
||||
m_blockHeaderSize = m_blockHeader.size();
|
||||
|
||||
m_pool->side_chain().fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares);
|
||||
m_sidechain->fill_sidechain_data(*m_poolBlockTemplate, miner_wallet, m_txkeySec, m_shares);
|
||||
|
||||
// Only choose transactions that were received 10 or more seconds ago
|
||||
size_t total_mempool_transactions;
|
||||
{
|
||||
m_mempoolTxs.clear();
|
||||
|
||||
ReadLock mempool_lock(mempool.m_lock);
|
||||
|
||||
total_mempool_transactions = mempool.m_transactions.size();
|
||||
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
|
||||
for (auto& it : mempool.m_transactions) {
|
||||
if (cur_time >= it.second.time_received + 10) {
|
||||
m_mempoolTxs.emplace_back(it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Safeguard for busy mempool moments
|
||||
// If the block template gets too big, nodes won't be able to send and receive it because of p2p packet size limit
|
||||
// Calculate how many transactions we can take
|
||||
{
|
||||
PoolBlock* b = m_poolBlockTemplate;
|
||||
b->m_transactions.clear();
|
||||
b->m_transactions.resize(1);
|
||||
b->m_outputs.clear();
|
||||
|
||||
// Block template size without coinbase outputs and transactions (add 1+1 more bytes for output and tx count if they go above 128)
|
||||
size_t k = b->serialize_mainchain_data().size() + b->serialize_sidechain_data().size() + 2;
|
||||
|
||||
// a rough estimation of outputs' size
|
||||
// all outputs have <= 5 bytes for each output's reward, and up to 18 outputs can have 6 bytes for output's reward
|
||||
k += m_shares.size() * (5 /* reward */ + 1 /* tx_type */ + HASH_SIZE /* stealth address */ + 1 /* viewtag */) + 18;
|
||||
|
||||
const size_t max_transactions = (MAX_BLOCK_TEMPLATE_SIZE > k) ? ((MAX_BLOCK_TEMPLATE_SIZE - k) / HASH_SIZE) : 0;
|
||||
|
||||
if (max_transactions == 0) {
|
||||
m_mempoolTxs.clear();
|
||||
}
|
||||
else if (m_mempoolTxs.size() > max_transactions) {
|
||||
std::nth_element(m_mempoolTxs.begin(), m_mempoolTxs.begin() + max_transactions, m_mempoolTxs.end());
|
||||
m_mempoolTxs.resize(max_transactions);
|
||||
}
|
||||
|
||||
LOGINFO(4, "mempool has " << total_mempool_transactions << " transactions, taking " << m_mempoolTxs.size() << " transactions from it");
|
||||
}
|
||||
|
||||
const uint64_t base_reward = get_base_reward(data.already_generated_coins);
|
||||
|
||||
uint64_t total_tx_fees = 0;
|
||||
uint64_t total_tx_weight = 0;
|
||||
for (const TxMempoolData& tx : m_mempoolTxs) {
|
||||
total_tx_fees += tx.fee;
|
||||
total_tx_weight += tx.weight;
|
||||
}
|
||||
|
||||
const uint64_t max_reward = base_reward + total_tx_fees;
|
||||
|
||||
LOGINFO(3, "base reward = " << log::Gray() << log::XMRAmount(base_reward) << log::NoColor() <<
|
||||
", " << log::Gray() << m_mempoolTxs.size() << log::NoColor() <<
|
||||
" transactions, fees = " << log::Gray() << log::XMRAmount(total_tx_fees) << log::NoColor() <<
|
||||
", weight = " << log::Gray() << total_tx_weight);
|
||||
|
||||
if (!SideChain::split_reward(max_reward, m_shares, m_rewards)) {
|
||||
use_old_template();
|
||||
return;
|
||||
@@ -336,8 +362,14 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
|
||||
m_numTransactionHashes = m_mempoolTxsOrder.size();
|
||||
m_transactionHashes.assign(HASH_SIZE, 0);
|
||||
m_transactionHashesSet.clear();
|
||||
m_transactionHashesSet.reserve(m_mempoolTxsOrder.size());
|
||||
for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
|
||||
const TxMempoolData& tx = m_mempoolTxs[m_mempoolTxsOrder[i]];
|
||||
if (!m_transactionHashesSet.insert(tx.id).second) {
|
||||
LOGERR(1, "Added transaction " << tx.id << " twice. Fix the code!");
|
||||
continue;
|
||||
}
|
||||
m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
|
||||
final_fees += tx.fee;
|
||||
final_weight += tx.weight;
|
||||
@@ -353,19 +385,14 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
// Sometimes it even finds the optimal solution
|
||||
|
||||
// Sort all transactions by fee per byte (highest to lowest)
|
||||
std::sort(m_mempoolTxsOrder.begin(), m_mempoolTxsOrder.end(),
|
||||
[this](int a, int b)
|
||||
{
|
||||
const TxMempoolData& tx_a = m_mempoolTxs[a];
|
||||
const TxMempoolData& tx_b = m_mempoolTxs[b];
|
||||
return tx_a.fee * tx_b.weight > tx_b.fee * tx_a.weight;
|
||||
});
|
||||
std::sort(m_mempoolTxsOrder.begin(), m_mempoolTxsOrder.end(), [this](int a, int b) { return m_mempoolTxs[a] < m_mempoolTxs[b]; });
|
||||
|
||||
final_reward = base_reward;
|
||||
final_fees = 0;
|
||||
final_weight = miner_tx_weight;
|
||||
|
||||
for (int i = 0; i < static_cast<int>(m_mempoolTxsOrder.size());) {
|
||||
m_mempoolTxsOrder2.clear();
|
||||
for (int i = 0; i < static_cast<int>(m_mempoolTxsOrder.size()); ++i) {
|
||||
const TxMempoolData& tx = m_mempoolTxs[m_mempoolTxsOrder[i]];
|
||||
|
||||
int k = -1;
|
||||
@@ -379,8 +406,10 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
|
||||
// Try replacing other transactions when we are above the limit
|
||||
if (final_weight + tx.weight > data.median_weight) {
|
||||
for (int j = 0; j < i; ++j) {
|
||||
const TxMempoolData& prev_tx = m_mempoolTxs[m_mempoolTxsOrder[j]];
|
||||
// Don't check more than 100 transactions deep because they have higher and higher fee/byte
|
||||
const int n = static_cast<int>(m_mempoolTxsOrder2.size());
|
||||
for (int j = n - 1, j1 = std::max<int>(0, n - 100); j >= j1; --j) {
|
||||
const TxMempoolData& prev_tx = m_mempoolTxs[m_mempoolTxsOrder2[j]];
|
||||
const uint64_t reward2 = get_block_reward(base_reward, data.median_weight, final_fees + tx.fee - prev_tx.fee, final_weight + tx.weight - prev_tx.weight);
|
||||
if (reward2 > final_reward) {
|
||||
// If replacing some other transaction increases the reward even more, remember it
|
||||
@@ -393,21 +422,19 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
|
||||
if (k == i) {
|
||||
// Simply adding this tx improves the reward
|
||||
m_mempoolTxsOrder2.push_back(m_mempoolTxsOrder[i]);
|
||||
final_fees += tx.fee;
|
||||
final_weight += tx.weight;
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (k >= 0) {
|
||||
else if (k >= 0) {
|
||||
// Replacing another tx with this tx improves the reward
|
||||
const TxMempoolData& prev_tx = m_mempoolTxs[m_mempoolTxsOrder[k]];
|
||||
const TxMempoolData& prev_tx = m_mempoolTxs[m_mempoolTxsOrder2[k]];
|
||||
m_mempoolTxsOrder2[k] = m_mempoolTxsOrder[i];
|
||||
final_fees += tx.fee - prev_tx.fee;
|
||||
final_weight += tx.weight - prev_tx.weight;
|
||||
}
|
||||
|
||||
m_mempoolTxsOrder.erase(m_mempoolTxsOrder.begin() + ((k >= 0) ? k : i));
|
||||
}
|
||||
m_mempoolTxsOrder = m_mempoolTxsOrder2;
|
||||
|
||||
final_fees = 0;
|
||||
final_weight = miner_tx_weight;
|
||||
@@ -416,8 +443,14 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
|
||||
m_numTransactionHashes = m_mempoolTxsOrder.size();
|
||||
m_transactionHashes.assign(HASH_SIZE, 0);
|
||||
m_transactionHashesSet.clear();
|
||||
m_transactionHashesSet.reserve(m_mempoolTxsOrder.size());
|
||||
for (size_t i = 0; i < m_mempoolTxsOrder.size(); ++i) {
|
||||
const TxMempoolData& tx = m_mempoolTxs[m_mempoolTxsOrder[i]];
|
||||
if (!m_transactionHashesSet.insert(tx.id).second) {
|
||||
LOGERR(1, "Added transaction " << tx.id << " twice. Fix the code!");
|
||||
continue;
|
||||
}
|
||||
m_transactionHashes.insert(m_transactionHashes.end(), tx.id.h, tx.id.h + HASH_SIZE);
|
||||
final_fees += tx.fee;
|
||||
final_weight += tx.weight;
|
||||
@@ -565,7 +598,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
|
||||
|
||||
PoolBlock check;
|
||||
const int result = check.deserialize(buf.data(), buf.size(), m_pool->side_chain(), nullptr);
|
||||
const int result = check.deserialize(buf.data(), buf.size(), *m_sidechain, nullptr, false);
|
||||
if (result != 0) {
|
||||
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
||||
}
|
||||
@@ -591,9 +624,11 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
|
||||
m_blockHeader.clear();
|
||||
m_minerTxExtra.clear();
|
||||
m_transactionHashes.clear();
|
||||
m_transactionHashesSet.clear();
|
||||
m_rewards.clear();
|
||||
m_mempoolTxs.clear();
|
||||
m_mempoolTxsOrder.clear();
|
||||
m_mempoolTxsOrder2.clear();
|
||||
}
|
||||
|
||||
#if TEST_MEMPOOL_PICKING_ALGORITHM
|
||||
@@ -800,7 +835,7 @@ hash BlockTemplate::calc_sidechain_hash() const
|
||||
const int sidechain_hash_offset = static_cast<int>(m_extraNonceOffsetInTemplate + m_poolBlockTemplate->m_extraNonceSize) + 2;
|
||||
const int blob_size = static_cast<int>(m_blockTemplateBlob.size());
|
||||
|
||||
const std::vector<uint8_t>& consensus_id = m_pool->side_chain().consensus_id();
|
||||
const std::vector<uint8_t>& consensus_id = m_sidechain->consensus_id();
|
||||
const std::vector<uint8_t> sidechain_data = m_poolBlockTemplate->serialize_sidechain_data();
|
||||
|
||||
keccak_custom([this, sidechain_hash_offset, blob_size, consensus_id, &sidechain_data](int offset) -> uint8_t {
|
||||
@@ -928,12 +963,13 @@ void BlockTemplate::calc_merkle_tree_main_branch()
|
||||
}
|
||||
}
|
||||
|
||||
bool BlockTemplate::get_difficulties(const uint32_t template_id, uint64_t& height, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const
|
||||
bool BlockTemplate::get_difficulties(const uint32_t template_id, uint64_t& height, uint64_t& sidechain_height, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const
|
||||
{
|
||||
ReadLock lock(m_lock);
|
||||
|
||||
if (template_id == m_templateId) {
|
||||
height = m_height;
|
||||
sidechain_height = m_poolBlockTemplate->m_sidechainHeight;
|
||||
mainchain_difficulty = m_difficulty;
|
||||
sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
|
||||
return true;
|
||||
@@ -942,7 +978,7 @@ bool BlockTemplate::get_difficulties(const uint32_t template_id, uint64_t& heigh
|
||||
const BlockTemplate* old = m_oldTemplates[template_id % array_size(&BlockTemplate::m_oldTemplates)];
|
||||
|
||||
if (old && (template_id == old->m_templateId)) {
|
||||
return old->get_difficulties(template_id, height, mainchain_difficulty, sidechain_difficulty);
|
||||
return old->get_difficulties(template_id, height, sidechain_height, mainchain_difficulty, sidechain_difficulty);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -971,11 +1007,12 @@ uint32_t BlockTemplate::get_hashing_blob(const uint32_t template_id, uint32_t ex
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t BlockTemplate::get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const
|
||||
uint32_t BlockTemplate::get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, uint64_t& sidechain_height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const
|
||||
{
|
||||
ReadLock lock(m_lock);
|
||||
|
||||
height = m_height;
|
||||
sidechain_height = m_poolBlockTemplate->m_sidechainHeight;
|
||||
difficulty = m_difficulty;
|
||||
sidechain_difficulty = m_poolBlockTemplate->m_difficulty;
|
||||
seed_hash = m_seedHash;
|
||||
@@ -1086,8 +1123,6 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
|
||||
m_poolBlockTemplate->m_nonce = nonce;
|
||||
m_poolBlockTemplate->m_extraNonce = extra_nonce;
|
||||
|
||||
SideChain& side_chain = m_pool->side_chain();
|
||||
|
||||
#if POOL_BLOCK_DEBUG
|
||||
{
|
||||
std::vector<uint8_t> buf = m_poolBlockTemplate->serialize_mainchain_data();
|
||||
@@ -1099,25 +1134,27 @@ void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce,
|
||||
buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end());
|
||||
|
||||
PoolBlock check;
|
||||
const int result = check.deserialize(buf.data(), buf.size(), side_chain, nullptr);
|
||||
const int result = check.deserialize(buf.data(), buf.size(), *m_sidechain, nullptr, false);
|
||||
if (result != 0) {
|
||||
LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result);
|
||||
}
|
||||
|
||||
hash pow_hash;
|
||||
if (!check.get_pow_hash(m_pool->hasher(), check.m_txinGenHeight, m_seedHash, pow_hash)) {
|
||||
LOGERR(1, "PoW check failed for the sidechain block. Fix it! ");
|
||||
}
|
||||
else if (!check.m_difficulty.check_pow(pow_hash)) {
|
||||
LOGERR(1, "Sidechain block has wrong PoW. Fix it! ");
|
||||
if (m_hasher) {
|
||||
hash pow_hash;
|
||||
if (!check.get_pow_hash(m_hasher, check.m_txinGenHeight, m_seedHash, pow_hash)) {
|
||||
LOGERR(1, "PoW check failed for the sidechain block. Fix it! ");
|
||||
}
|
||||
else if (!check.m_difficulty.check_pow(pow_hash)) {
|
||||
LOGERR(1, "Sidechain block has wrong PoW. Fix it! ");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
m_poolBlockTemplate->m_verified = true;
|
||||
if (!side_chain.block_seen(*m_poolBlockTemplate)) {
|
||||
if (!m_sidechain->block_seen(*m_poolBlockTemplate)) {
|
||||
m_poolBlockTemplate->m_wantBroadcast = true;
|
||||
side_chain.add_block(*m_poolBlockTemplate);
|
||||
m_sidechain->add_block(*m_poolBlockTemplate);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
+12
-5
@@ -23,7 +23,8 @@
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
class p2pool;
|
||||
class SideChain;
|
||||
class RandomX_Hasher_Base;
|
||||
class Mempool;
|
||||
class Wallet;
|
||||
struct PoolBlock;
|
||||
@@ -32,18 +33,19 @@ struct MinerShare;
|
||||
class BlockTemplate
|
||||
{
|
||||
public:
|
||||
explicit BlockTemplate(p2pool* pool);
|
||||
explicit BlockTemplate(SideChain* sidechain, RandomX_Hasher_Base* hasher);
|
||||
~BlockTemplate();
|
||||
|
||||
BlockTemplate(const BlockTemplate& b);
|
||||
BlockTemplate& operator=(const BlockTemplate& b);
|
||||
|
||||
void update(const MinerData& data, const Mempool& mempool, Wallet* miner_wallet);
|
||||
uint64_t last_updated() const { return m_lastUpdated.load(); }
|
||||
|
||||
bool get_difficulties(const uint32_t template_id, uint64_t& height, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
|
||||
bool get_difficulties(const uint32_t template_id, uint64_t& height, uint64_t& sidechain_height, difficulty_type& mainchain_difficulty, difficulty_type& sidechain_difficulty) const;
|
||||
uint32_t get_hashing_blob(const uint32_t template_id, uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset) const;
|
||||
|
||||
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
|
||||
uint32_t get_hashing_blob(uint32_t extra_nonce, uint8_t (&blob)[128], uint64_t& height, uint64_t& sidechain_height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
|
||||
uint32_t get_hashing_blobs(uint32_t extra_nonce_start, uint32_t count, std::vector<uint8_t>& blobs, uint64_t& height, difficulty_type& difficulty, difficulty_type& sidechain_difficulty, hash& seed_hash, size_t& nonce_offset, uint32_t& template_id) const;
|
||||
|
||||
std::vector<uint8_t> get_block_template_blob(uint32_t template_id, size_t& nonce_offset, size_t& extra_nonce_offset) const;
|
||||
@@ -54,9 +56,11 @@ public:
|
||||
void submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce);
|
||||
|
||||
FORCEINLINE const std::vector<MinerShare>& shares() const { return m_shares; }
|
||||
FORCEINLINE const PoolBlock* pool_block_template() const { return m_poolBlockTemplate; }
|
||||
|
||||
private:
|
||||
p2pool* m_pool;
|
||||
SideChain* m_sidechain;
|
||||
RandomX_Hasher_Base* m_hasher;
|
||||
|
||||
private:
|
||||
int create_miner_tx(const MinerData& data, const std::vector<MinerShare>& shares, uint64_t max_reward_amounts_weight, bool dry_run);
|
||||
@@ -69,6 +73,7 @@ private:
|
||||
mutable uv_rwlock_t m_lock;
|
||||
|
||||
uint32_t m_templateId;
|
||||
std::atomic<uint64_t> m_lastUpdated;
|
||||
|
||||
std::vector<uint8_t> m_blockTemplateBlob;
|
||||
std::vector<uint8_t> m_merkleTreeMainBranch;
|
||||
@@ -101,9 +106,11 @@ private:
|
||||
std::vector<uint8_t> m_blockHeader;
|
||||
std::vector<uint8_t> m_minerTxExtra;
|
||||
std::vector<uint8_t> m_transactionHashes;
|
||||
unordered_set<hash> m_transactionHashesSet;
|
||||
std::vector<uint64_t> m_rewards;
|
||||
std::vector<TxMempoolData> m_mempoolTxs;
|
||||
std::vector<int> m_mempoolTxsOrder;
|
||||
std::vector<int> m_mempoolTxsOrder2;
|
||||
std::vector<MinerShare> m_shares;
|
||||
|
||||
std::mt19937_64 m_rng;
|
||||
|
||||
+108
-6
@@ -19,7 +19,7 @@
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#pragma warning(disable : 4005 4061 4365 4464 4625 4626 4668 4710 4711 4804 4820 5039 5045 5220 5246)
|
||||
#pragma warning(disable : 4005 4061 4324 4365 4464 4625 4626 4668 4710 4711 4804 4820 5039 5045 5220 5246)
|
||||
#define FORCEINLINE __forceinline
|
||||
#define NOINLINE __declspec(noinline)
|
||||
#define LIKELY(expression) expression
|
||||
@@ -109,6 +109,8 @@ constexpr uint8_t TX_EXTRA_MERGE_MINING_TAG = 3;
|
||||
#ifdef _MSC_VER
|
||||
#define umul128 _umul128
|
||||
#define udiv128 _udiv128
|
||||
FORCEINLINE uint64_t shiftleft128(uint64_t lo, uint64_t hi, uint64_t shift) { return __shiftleft128(lo, hi, static_cast<unsigned char>(shift)); }
|
||||
FORCEINLINE uint64_t shiftright128(uint64_t lo, uint64_t hi, uint64_t shift) { return __shiftright128(lo, hi, static_cast<unsigned char>(shift)); }
|
||||
#else
|
||||
FORCEINLINE uint64_t umul128(uint64_t a, uint64_t b, uint64_t* hi)
|
||||
{
|
||||
@@ -126,13 +128,16 @@ FORCEINLINE uint64_t udiv128(uint64_t hi, uint64_t lo, uint64_t divisor, uint64_
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FORCEINLINE uint64_t shiftleft128(uint64_t lo, uint64_t hi, uint64_t shift) { return (hi << shift) | (lo >> (64 - shift)); }
|
||||
FORCEINLINE uint64_t shiftright128(uint64_t lo, uint64_t hi, uint64_t shift) { return (hi << (64 - shift)) | (lo >> shift); }
|
||||
#endif
|
||||
|
||||
template<typename T> FORCEINLINE T round_up(T a, size_t granularity) { return static_cast<T>(((a + (granularity - static_cast<size_t>(1))) / granularity) * granularity); }
|
||||
template<typename T> constexpr FORCEINLINE T round_up(T a, size_t granularity) { return static_cast<T>(((a + (granularity - static_cast<size_t>(1))) / granularity) * granularity); }
|
||||
|
||||
struct hash
|
||||
struct alignas(uint64_t) hash
|
||||
{
|
||||
alignas(8) uint8_t h[HASH_SIZE];
|
||||
uint8_t h[HASH_SIZE];
|
||||
|
||||
FORCEINLINE hash() : h{} {}
|
||||
|
||||
@@ -174,7 +179,11 @@ struct hash
|
||||
static_assert(sizeof(hash) == HASH_SIZE, "struct hash has invalid size, check your compiler options");
|
||||
static_assert(std::is_standard_layout<hash>::value, "struct hash is not a POD, check your compiler options");
|
||||
|
||||
struct difficulty_type
|
||||
struct
|
||||
#ifdef __GNUC__
|
||||
alignas(unsigned __int128)
|
||||
#endif
|
||||
difficulty_type
|
||||
{
|
||||
FORCEINLINE difficulty_type() : lo(0), hi(0) {}
|
||||
FORCEINLINE difficulty_type(uint64_t a, uint64_t b) : lo(a), hi(b) {}
|
||||
@@ -197,6 +206,47 @@ struct difficulty_type
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCEINLINE difficulty_type& operator+=(uint64_t b) { return operator+=(difficulty_type{ b, 0 }); }
|
||||
|
||||
FORCEINLINE difficulty_type& operator-=(const difficulty_type& b)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
_subborrow_u64(_subborrow_u64(0, lo, b.lo, &lo), hi, b.hi, &hi);
|
||||
#elif __GNUC__
|
||||
*reinterpret_cast<unsigned __int128*>(this) -= *reinterpret_cast<const unsigned __int128*>(&b);
|
||||
#else
|
||||
const uint64_t t = b.lo;
|
||||
const uint64_t carry = (lo < t) ? 1 : 0;
|
||||
lo -= t;
|
||||
hi -= b.hi + carry;
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCEINLINE difficulty_type& operator-=(uint64_t b) { return operator-=(difficulty_type{ b, 0 }); }
|
||||
|
||||
FORCEINLINE difficulty_type& operator*=(const uint64_t b)
|
||||
{
|
||||
uint64_t t;
|
||||
lo = umul128(lo, b, &t);
|
||||
hi = t + hi * b;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
FORCEINLINE difficulty_type& operator/=(const uint64_t b)
|
||||
{
|
||||
const uint64_t t = hi;
|
||||
hi = t / b;
|
||||
|
||||
uint64_t r;
|
||||
lo = udiv128(t % b, lo, b, &r);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
difficulty_type& operator/=(difficulty_type b);
|
||||
|
||||
FORCEINLINE bool operator<(const difficulty_type& other) const
|
||||
{
|
||||
if (hi < other.hi) return true;
|
||||
@@ -244,12 +294,58 @@ struct difficulty_type
|
||||
static_assert(sizeof(difficulty_type) == sizeof(uint64_t) * 2, "struct difficulty_type has invalid size, check your compiler options");
|
||||
static_assert(std::is_standard_layout<difficulty_type>::value, "struct difficulty_type is not a POD, check your compiler options");
|
||||
|
||||
difficulty_type operator+(const difficulty_type& a, const difficulty_type& b);
|
||||
template<typename T>
|
||||
FORCEINLINE difficulty_type operator+(const difficulty_type& a, const T& b)
|
||||
{
|
||||
difficulty_type result = a;
|
||||
result += b;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE difficulty_type operator-(const difficulty_type& a, const T& b)
|
||||
{
|
||||
difficulty_type result = a;
|
||||
result -= b;
|
||||
return result;
|
||||
}
|
||||
|
||||
FORCEINLINE difficulty_type operator*(const difficulty_type& a, uint64_t b)
|
||||
{
|
||||
difficulty_type result = a;
|
||||
result *= b;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
FORCEINLINE difficulty_type operator/(const difficulty_type& a, const T& b)
|
||||
{
|
||||
difficulty_type result = a;
|
||||
result /= b;
|
||||
return result;
|
||||
}
|
||||
|
||||
struct TxMempoolData
|
||||
{
|
||||
FORCEINLINE TxMempoolData() : id(), blob_size(0), weight(0), fee(0), time_received(0) {}
|
||||
|
||||
FORCEINLINE bool operator<(const TxMempoolData& tx) const
|
||||
{
|
||||
const uint64_t a = fee * tx.weight;
|
||||
const uint64_t b = tx.fee * weight;
|
||||
|
||||
// Prefer transactions with higher fee/byte
|
||||
if (a > b) return true;
|
||||
if (a < b) return false;
|
||||
|
||||
// If fee/byte is the same, prefer smaller transactions (they give smaller penalty when going above the median block size limit)
|
||||
if (weight < tx.weight) return true;
|
||||
if (weight > tx.weight) return false;
|
||||
|
||||
// If two transactions have exactly the same fee and weight, just order them by id
|
||||
return id < tx.id;
|
||||
}
|
||||
|
||||
hash id;
|
||||
uint64_t blob_size;
|
||||
uint64_t weight;
|
||||
@@ -333,6 +429,12 @@ struct raw_ip
|
||||
|
||||
return (*this == localhost_ipv4) || (*this == localhost_ipv6);
|
||||
}
|
||||
|
||||
FORCEINLINE bool is_ipv4_prefix() const
|
||||
{
|
||||
alignas(8) static constexpr uint8_t ipv4_prefix[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff };
|
||||
return memcmp(data, ipv4_prefix, sizeof(ipv4_prefix)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(raw_ip) == 16, "struct raw_ip has invalid size");
|
||||
|
||||
@@ -117,8 +117,8 @@ static cmd cmds[] = {
|
||||
{ STRCONST("peers"), "", "show all peers", do_showpeers },
|
||||
{ STRCONST("workers"), "", "show all connected workers", do_showworkers },
|
||||
{ STRCONST("bans"), "", "show all banned IPs", do_showbans },
|
||||
{ STRCONST("outpeers"), "", "set maximum number of outgoing connections", do_outpeers },
|
||||
{ STRCONST("inpeers"), "", "set maximum number of incoming connections", do_inpeers },
|
||||
{ STRCONST("outpeers"), "<N>", "set maximum number of outgoing connections", do_outpeers },
|
||||
{ STRCONST("inpeers"), "<N>", "set maximum number of incoming connections", do_inpeers },
|
||||
#ifdef WITH_RANDOMX
|
||||
{ STRCONST("start_mining"), "<threads>", "start mining", do_start_mining },
|
||||
{ STRCONST("stop_mining"), "", "stop mining", do_stop_mining },
|
||||
@@ -131,7 +131,7 @@ static void do_help(p2pool * /* m_pool */, const char * /* args */)
|
||||
{
|
||||
LOGINFO(0, "List of commands");
|
||||
for (int i = 0; cmds[i].name.len; ++i) {
|
||||
LOGINFO(0, cmds[i].name.str << " " << cmds[i].arg << "\t" << cmds[i].descr);
|
||||
LOGINFO(0, log::pad_right(cmds[i].name.str, 20) << log::pad_right(cmds[i].arg, 12) << cmds[i].descr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +266,19 @@ void ConsoleCommands::stdinReadCallback(uv_stream_t* stream, ssize_t nread, cons
|
||||
for (; c->name.len; ++c) {
|
||||
if (!strncmp(command.c_str(), c->name.str, c->name.len)) {
|
||||
const char* args = (c->name.len + 1 <= k) ? (command.c_str() + c->name.len + 1) : "";
|
||||
|
||||
// Skip spaces
|
||||
while ((args[0] == ' ') || (args[0] == '\t')) {
|
||||
++args;
|
||||
}
|
||||
|
||||
// Check if an argument is required
|
||||
if (strlen(c->arg) && !strlen(args)) {
|
||||
LOGWARN(0, c->name.str << " requires arguments");
|
||||
do_help(nullptr, nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
c->func(pThis->m_pool, args);
|
||||
break;
|
||||
}
|
||||
@@ -273,6 +286,7 @@ void ConsoleCommands::stdinReadCallback(uv_stream_t* stream, ssize_t nread, cons
|
||||
|
||||
if (!c->name.len) {
|
||||
LOGWARN(0, "Unknown command " << command.c_str());
|
||||
do_help(nullptr, nullptr);
|
||||
}
|
||||
|
||||
k = command.find_first_not_of("\r\n", k + 1);
|
||||
|
||||
+44
-9
@@ -217,12 +217,8 @@ public:
|
||||
{
|
||||
WriteLock lock(derivations_lock);
|
||||
|
||||
DerivationEntry& entry = derivations->emplace(index, DerivationEntry{ derivation, {} }).first->second;
|
||||
|
||||
const uint32_t k = static_cast<uint32_t>(output_index << 8) | view_tag;
|
||||
if (std::find(entry.m_viewTags.begin(), entry.m_viewTags.end(), k) == entry.m_viewTags.end()) {
|
||||
entry.m_viewTags.emplace_back(k);
|
||||
}
|
||||
DerivationEntry& entry = derivations->emplace(index, DerivationEntry{ derivation, { 0xFFFFFFFFUL, 0xFFFFFFFFUL }, {} }).first->second;
|
||||
entry.add_view_tag(static_cast<uint32_t>(output_index << 8) | view_tag);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -308,16 +304,19 @@ public:
|
||||
WriteLock lock(derivations_lock);
|
||||
delete derivations;
|
||||
derivations = new DerivationsMap();
|
||||
derivations->reserve(5000);
|
||||
}
|
||||
{
|
||||
WriteLock lock(public_keys_lock);
|
||||
delete public_keys;
|
||||
public_keys = new PublicKeysMap();
|
||||
public_keys->reserve(5000);
|
||||
}
|
||||
{
|
||||
WriteLock lock(tx_keys_lock);
|
||||
delete tx_keys;
|
||||
tx_keys = new TxKeysMap();
|
||||
tx_keys->reserve(50);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,10 +324,24 @@ private:
|
||||
struct DerivationEntry
|
||||
{
|
||||
hash m_derivation;
|
||||
std::vector<uint32_t> m_viewTags;
|
||||
uint32_t m_viewTags1[2] = { 0xFFFFFFFFUL, 0xFFFFFFFFUL };
|
||||
std::vector<uint32_t> m_viewTags2;
|
||||
|
||||
bool find_view_tag(size_t output_index, uint8_t& view_tag) const {
|
||||
for (uint32_t k : m_viewTags) {
|
||||
FORCEINLINE bool find_view_tag(size_t output_index, uint8_t& view_tag) const
|
||||
{
|
||||
#define ITER(i) do { \
|
||||
const uint32_t k = m_viewTags1[i]; \
|
||||
if ((k >> 8) == output_index) { \
|
||||
view_tag = static_cast<uint8_t>(k); \
|
||||
return true; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
ITER(0);
|
||||
ITER(1);
|
||||
#undef ITER
|
||||
|
||||
for (const uint32_t k : m_viewTags2) {
|
||||
if ((k >> 8) == output_index) {
|
||||
view_tag = static_cast<uint8_t>(k);
|
||||
return true;
|
||||
@@ -336,6 +349,28 @@ private:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FORCEINLINE void add_view_tag(uint32_t k)
|
||||
{
|
||||
#define ITER(i) do { \
|
||||
const uint32_t t = m_viewTags1[i]; \
|
||||
if (t == 0xFFFFFFFFUL) { \
|
||||
m_viewTags1[i] = k; \
|
||||
return; \
|
||||
} \
|
||||
else if (t == k) { \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
ITER(0);
|
||||
ITER(1);
|
||||
#undef ITER
|
||||
|
||||
if (std::find(m_viewTags2.begin(), m_viewTags2.end(), k) == m_viewTags2.end()) {
|
||||
m_viewTags2.emplace_back(k);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef unordered_map<std::array<uint8_t, HASH_SIZE * 2>, DerivationEntry> DerivationsMap;
|
||||
|
||||
+65
-15
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "uv_util.h"
|
||||
#include "wallet.h"
|
||||
#include <ctime>
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
@@ -33,9 +34,6 @@ bool CONSOLE_COLORS = true;
|
||||
|
||||
#ifndef P2POOL_LOG_DISABLE
|
||||
|
||||
static std::atomic<bool> stopped{ false };
|
||||
static volatile bool worker_started = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
static const HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
static const HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
@@ -54,6 +52,8 @@ public:
|
||||
FORCEINLINE Worker()
|
||||
: m_writePos(0)
|
||||
, m_readPos(0)
|
||||
, m_started{ false }
|
||||
, m_stopped(false)
|
||||
{
|
||||
set_main_thread();
|
||||
|
||||
@@ -73,15 +73,14 @@ public:
|
||||
abort();
|
||||
}
|
||||
|
||||
while (!worker_started) {
|
||||
while (m_started.load() == false) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleMode(hStdIn, ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_EXTENDED_FLAGS);
|
||||
|
||||
DWORD dwConsoleMode;
|
||||
if (GetConsoleMode(hStdIn, &dwConsoleMode)) {
|
||||
SetConsoleMode(hStdIn, dwConsoleMode & ~ENABLE_QUICK_EDIT_MODE);
|
||||
}
|
||||
if (GetConsoleMode(hStdOut, &dwConsoleMode)) {
|
||||
SetConsoleMode(hStdOut, dwConsoleMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
}
|
||||
@@ -92,6 +91,8 @@ public:
|
||||
if (!m_logFile.is_open()) {
|
||||
LOGERR(0, "failed to open " << log_file_name);
|
||||
}
|
||||
|
||||
init_uv_threadpool();
|
||||
}
|
||||
|
||||
~Worker()
|
||||
@@ -105,16 +106,25 @@ public:
|
||||
|
||||
FORCEINLINE void stop()
|
||||
{
|
||||
if (stopped.exchange(true)) {
|
||||
return;
|
||||
{
|
||||
MutexLock lock(m_mutex);
|
||||
if (m_stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_stopped = true;
|
||||
LOGINFO(0, "stopped");
|
||||
}
|
||||
|
||||
LOGINFO(0, "stopped");
|
||||
uv_thread_join(&m_worker);
|
||||
uv_cond_destroy(&m_cond);
|
||||
uv_mutex_destroy(&m_mutex);
|
||||
uv_loop_close(uv_default_loop());
|
||||
|
||||
#if ((UV_VERSION_MAJOR > 1) || ((UV_VERSION_MAJOR == 1) && (UV_VERSION_MINOR >= 38)))
|
||||
uv_library_shutdown();
|
||||
#endif
|
||||
|
||||
m_logFile.close();
|
||||
}
|
||||
|
||||
@@ -143,17 +153,47 @@ public:
|
||||
uv_cond_signal(&m_cond);
|
||||
}
|
||||
|
||||
private:
|
||||
static void init_uv_threadpool()
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
#define putenv _putenv
|
||||
#endif
|
||||
|
||||
const uint32_t N = std::max(std::min(std::thread::hardware_concurrency(), 4U), 8U);
|
||||
|
||||
char buf[40] = {};
|
||||
log::Stream s(buf);
|
||||
s << "UV_THREADPOOL_SIZE=" << N << '\0';
|
||||
|
||||
int err = putenv(buf);
|
||||
if (err != 0) {
|
||||
err = errno;
|
||||
LOGWARN(0, "Couldn't set UV thread pool size to " << N << " threads, putenv returned error " << err);
|
||||
}
|
||||
|
||||
static uv_work_t dummy;
|
||||
err = uv_queue_work(uv_default_loop_checked(), &dummy, [](uv_work_t*) {}, nullptr);
|
||||
if (err) {
|
||||
LOGERR(0, "init_uv_threadpool: uv_queue_work failed, error " << uv_err_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static void run_wrapper(void* arg) { reinterpret_cast<Worker*>(arg)->run(); }
|
||||
|
||||
NOINLINE void run()
|
||||
{
|
||||
worker_started = true;
|
||||
m_started.exchange(true);
|
||||
|
||||
do {
|
||||
uv_mutex_lock(&m_mutex);
|
||||
if (m_readPos == m_writePos.load()) {
|
||||
// Nothing to do, wait for the signal
|
||||
// Nothing to do, wait for the signal or exit if stopped
|
||||
if (m_stopped) {
|
||||
uv_mutex_unlock(&m_mutex);
|
||||
return;
|
||||
}
|
||||
uv_cond_wait(&m_cond, &m_mutex);
|
||||
}
|
||||
uv_mutex_unlock(&m_mutex);
|
||||
@@ -217,7 +257,7 @@ private:
|
||||
severity = '\0';
|
||||
|
||||
m_readPos += SLOT_SIZE;
|
||||
} while (m_readPos < writePos);
|
||||
} while (m_readPos != writePos);
|
||||
}
|
||||
|
||||
// Flush the log file only after all pending log lines have been written
|
||||
@@ -231,7 +271,7 @@ private:
|
||||
m_logFile.open(log_file_name, std::ios::app | std::ios::binary);
|
||||
}
|
||||
}
|
||||
} while (!stopped);
|
||||
} while (1);
|
||||
}
|
||||
|
||||
static FORCEINLINE void strip_colors(char* buf, uint32_t& size)
|
||||
@@ -269,6 +309,9 @@ private:
|
||||
uv_mutex_t m_mutex;
|
||||
uv_thread_t m_worker;
|
||||
|
||||
std::atomic<bool> m_started;
|
||||
bool m_stopped;
|
||||
|
||||
std::ofstream m_logFile;
|
||||
};
|
||||
|
||||
@@ -336,7 +379,7 @@ NOINLINE void Stream::writeCurrentTime()
|
||||
m_numberWidth = 1;
|
||||
}
|
||||
|
||||
NOINLINE void put_rawip(const raw_ip& value, Stream* wrapper)
|
||||
NOINLINE void Stream::Entry<raw_ip>::put(const raw_ip& value, Stream* wrapper)
|
||||
{
|
||||
const char* addr_str;
|
||||
char addr_str_buf[64];
|
||||
@@ -359,6 +402,13 @@ NOINLINE void put_rawip(const raw_ip& value, Stream* wrapper)
|
||||
}
|
||||
}
|
||||
|
||||
NOINLINE void Stream::Entry<Wallet>::put(const Wallet& w, Stream* wrapper)
|
||||
{
|
||||
char buf[Wallet::ADDRESS_LENGTH];
|
||||
w.encode(buf);
|
||||
wrapper->writeBuf(buf, Wallet::ADDRESS_LENGTH);
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
class Wallet;
|
||||
|
||||
namespace log {
|
||||
|
||||
extern int GLOBAL_LOG_LEVEL;
|
||||
@@ -451,12 +453,8 @@ struct log::Stream::Entry<PadRight<T>>
|
||||
}
|
||||
};
|
||||
|
||||
void put_rawip(const raw_ip& value, Stream* wrapper);
|
||||
|
||||
template<> struct log::Stream::Entry<raw_ip>
|
||||
{
|
||||
static FORCEINLINE void put(const raw_ip& value, Stream* wrapper) { put_rawip(value, wrapper); }
|
||||
};
|
||||
template<> struct log::Stream::Entry<raw_ip> { static NOINLINE void put(const raw_ip& value, Stream* wrapper); };
|
||||
template<> struct log::Stream::Entry<Wallet> { static NOINLINE void put(const Wallet& w, Stream* wrapper); };
|
||||
|
||||
namespace {
|
||||
template<log::Severity severity> void apply_severity(log::Stream&);
|
||||
|
||||
+6
-4
@@ -41,12 +41,14 @@ Miner::Miner(p2pool* pool, uint32_t threads)
|
||||
, m_startTimestamp(high_resolution_clock::now())
|
||||
, m_nonce(0)
|
||||
, m_nonceTimestamp(m_startTimestamp)
|
||||
, m_extraNonce(static_cast<uint32_t>(pool->p2p_server()->get_random64()))
|
||||
, m_totalHashes(0)
|
||||
, m_sharesFound(0)
|
||||
, m_job{}
|
||||
, m_jobIndex{ 0 }
|
||||
{
|
||||
std::random_device rd;
|
||||
m_extraNonce = static_cast<uint32_t>(rd());
|
||||
|
||||
on_block(m_pool->block_template());
|
||||
|
||||
m_minerThreads.reserve(threads);
|
||||
@@ -92,7 +94,7 @@ void Miner::on_block(const BlockTemplate& block)
|
||||
const uint32_t next_index = m_jobIndex ^ 1;
|
||||
Job& j = m_job[next_index];
|
||||
hash seed;
|
||||
j.m_blobSize = block.get_hashing_blob(m_extraNonce, j.m_blob, j.m_height, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
|
||||
j.m_blobSize = block.get_hashing_blob(m_extraNonce, j.m_blob, j.m_height, j.m_sidechainHeight, j.m_diff, j.m_sidechainDiff, seed, j.m_nonceOffset, j.m_templateId);
|
||||
|
||||
const uint32_t hash_count = 0 - m_nonce.exchange(0);
|
||||
m_jobIndex = next_index;
|
||||
@@ -204,12 +206,12 @@ void Miner::run(WorkerData* data)
|
||||
randomx_calculate_hash_next(vm, job[index].m_blob, job[index].m_blobSize, &h);
|
||||
|
||||
if (j.m_diff.check_pow(h)) {
|
||||
LOGINFO(0, log::Green() << "worker thread " << data->m_index << '/' << data->m_count << " found a mainchain block, submitting it");
|
||||
LOGINFO(0, log::Green() << "worker thread " << data->m_index << '/' << data->m_count << " found a mainchain block at height " << j.m_height << ", submitting it");
|
||||
m_pool->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce);
|
||||
}
|
||||
|
||||
if (j.m_sidechainDiff.check_pow(h)) {
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << j.m_height << ", diff " << j.m_sidechainDiff << ", worker thread " << data->m_index << '/' << data->m_count);
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << j.m_height << ", sidechain height " << j.m_sidechainHeight << ", diff " << j.m_sidechainDiff << ", worker thread " << data->m_index << '/' << data->m_count);
|
||||
m_pool->submit_sidechain_block(j.m_templateId, j.m_nonce, j.m_extraNonce);
|
||||
++m_sharesFound;
|
||||
}
|
||||
|
||||
+2
-1
@@ -56,7 +56,7 @@ private:
|
||||
|
||||
std::atomic<uint32_t> m_nonce;
|
||||
std::chrono::high_resolution_clock::time_point m_nonceTimestamp;
|
||||
const uint32_t m_extraNonce;
|
||||
uint32_t m_extraNonce;
|
||||
|
||||
std::atomic<uint64_t> m_totalHashes;
|
||||
std::atomic<uint32_t> m_sharesFound;
|
||||
@@ -69,6 +69,7 @@ private:
|
||||
difficulty_type m_diff = {};
|
||||
difficulty_type m_sidechainDiff = {};
|
||||
uint64_t m_height = 0;
|
||||
uint64_t m_sidechainHeight = 0;
|
||||
size_t m_nonceOffset = 0;
|
||||
uint32_t m_nonce = 0;
|
||||
uint32_t m_extraNonce = 0;
|
||||
|
||||
+215
-51
@@ -25,6 +25,7 @@
|
||||
#include "block_cache.h"
|
||||
#include "json_rpc_request.h"
|
||||
#include "json_parsers.h"
|
||||
#include "block_template.h"
|
||||
#include <rapidjson/document.h>
|
||||
#include <fstream>
|
||||
#include <numeric>
|
||||
@@ -85,7 +86,6 @@ P2PServer::P2PServer(p2pool* pool)
|
||||
set_max_outgoing_peers(params.m_maxOutgoingPeers);
|
||||
set_max_incoming_peers(params.m_maxIncomingPeers);
|
||||
|
||||
uv_mutex_init_checked(&m_rngLock);
|
||||
uv_mutex_init_checked(&m_blockLock);
|
||||
uv_mutex_init_checked(&m_peerListLock);
|
||||
uv_mutex_init_checked(&m_broadcastLock);
|
||||
@@ -141,7 +141,6 @@ P2PServer::~P2PServer()
|
||||
{
|
||||
shutdown_tcp();
|
||||
|
||||
uv_mutex_destroy(&m_rngLock);
|
||||
uv_mutex_destroy(&m_blockLock);
|
||||
uv_mutex_destroy(&m_peerListLock);
|
||||
uv_mutex_destroy(&m_broadcastLock);
|
||||
@@ -364,10 +363,9 @@ void P2PServer::update_peer_connections()
|
||||
|
||||
void P2PServer::update_peer_list()
|
||||
{
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
|
||||
MutexLock lock(m_clientsListLock);
|
||||
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
if (client->is_good() && (cur_time >= client->m_nextOutgoingPeerListRequest)) {
|
||||
send_peer_list_request(client, cur_time);
|
||||
@@ -419,13 +417,13 @@ void P2PServer::save_peer_list_async()
|
||||
const int err = uv_queue_work(&m_loop, &work->req,
|
||||
[](uv_work_t* req)
|
||||
{
|
||||
bkg_jobs_tracker.start("P2PServer::save_peer_list_async");
|
||||
BACKGROUND_JOB_START(P2PServer::save_peer_list_async);
|
||||
reinterpret_cast<Work*>(req->data)->server->save_peer_list();
|
||||
},
|
||||
[](uv_work_t* req, int /*status*/)
|
||||
{
|
||||
delete reinterpret_cast<Work*>(req->data);
|
||||
bkg_jobs_tracker.stop("P2PServer::save_peer_list_async");
|
||||
BACKGROUND_JOB_STOP(P2PServer::save_peer_list_async);
|
||||
});
|
||||
|
||||
if (err) {
|
||||
@@ -725,7 +723,7 @@ void P2PServer::remove_peer_from_list(const raw_ip& ip)
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::broadcast(const PoolBlock& block)
|
||||
void P2PServer::broadcast(const PoolBlock& block, const PoolBlock* parent)
|
||||
{
|
||||
MinerData miner_data = m_pool->miner_data();
|
||||
|
||||
@@ -765,13 +763,48 @@ void P2PServer::broadcast(const PoolBlock& block)
|
||||
writeVarint(outputs_blob_size, data->pruned_blob);
|
||||
|
||||
data->pruned_blob.insert(data->pruned_blob.end(), mainchain_data.begin() + outputs_offset + outputs_blob_size, mainchain_data.end());
|
||||
|
||||
const size_t N = block.m_transactions.size();
|
||||
if ((N > 1) && parent && (parent->m_transactions.size() > 1)) {
|
||||
unordered_map<hash, size_t> parent_transactions;
|
||||
parent_transactions.reserve(parent->m_transactions.size());
|
||||
|
||||
for (size_t i = 1; i < parent->m_transactions.size(); ++i) {
|
||||
parent_transactions.emplace(parent->m_transactions[i], i);
|
||||
}
|
||||
|
||||
// Reserve 1 additional byte per transaction to be ready for the worst case (all transactions are different in the parent block)
|
||||
data->compact_blob.reserve(data->pruned_blob.capacity() + (N - 1));
|
||||
|
||||
// Copy pruned_blob without the transaction list
|
||||
data->compact_blob.assign(data->pruned_blob.begin(), data->pruned_blob.end() - (N - 1) * HASH_SIZE);
|
||||
|
||||
// Process transaction hashes one by one
|
||||
size_t num_found = 0;
|
||||
for (size_t i = 1; i < N; ++i) {
|
||||
const hash& tx = block.m_transactions[i];
|
||||
auto it = parent_transactions.find(tx);
|
||||
if (it != parent_transactions.end()) {
|
||||
writeVarint(it->second, data->compact_blob);
|
||||
++num_found;
|
||||
}
|
||||
else {
|
||||
data->compact_blob.push_back(0);
|
||||
data->compact_blob.insert(data->compact_blob.end(), tx.h, tx.h + HASH_SIZE);
|
||||
}
|
||||
}
|
||||
LOGINFO(6, "compact blob: " << num_found << '/' << (N - 1) << " transactions were found in the parent block");
|
||||
|
||||
data->compact_blob.insert(data->compact_blob.end(), sidechain_data.begin(), sidechain_data.end());
|
||||
}
|
||||
|
||||
data->pruned_blob.insert(data->pruned_blob.end(), sidechain_data.begin(), sidechain_data.end());
|
||||
|
||||
data->ancestor_hashes.reserve(block.m_uncles.size() + 1);
|
||||
data->ancestor_hashes = block.m_uncles;
|
||||
data->ancestor_hashes.push_back(block.m_parent);
|
||||
|
||||
LOGINFO(5, "Broadcasting block " << block.m_sidechainId << " (height " << block.m_sidechainHeight << "): " << data->pruned_blob.size() << '/' << data->blob.size() << " bytes (pruned/full)");
|
||||
LOGINFO(5, "Broadcasting block " << block.m_sidechainId << " (height " << block.m_sidechainHeight << "): " << data->compact_blob.size() << '/' << data->pruned_blob.size() << '/' << data->blob.size() << " bytes (compact/pruned/full)");
|
||||
|
||||
{
|
||||
MutexLock lock(m_broadcastLock);
|
||||
@@ -839,6 +872,7 @@ void P2PServer::on_broadcast()
|
||||
uint8_t* p = p0;
|
||||
|
||||
bool send_pruned = true;
|
||||
bool send_compact = (client->m_protocolVersion >= PROTOCOL_VERSION_1_1) && !data->compact_blob.empty() && (data->compact_blob.size() < data->pruned_blob.size());
|
||||
|
||||
const hash* a = client->m_broadcastedHashes;
|
||||
const hash* b = client->m_broadcastedHashes + array_size(&P2PClient::m_broadcastedHashes);
|
||||
@@ -846,25 +880,27 @@ void P2PServer::on_broadcast()
|
||||
for (const hash& id : data->ancestor_hashes) {
|
||||
if (std::find(a, b, id) == b) {
|
||||
send_pruned = false;
|
||||
send_compact = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (send_pruned) {
|
||||
LOGINFO(6, "sending BLOCK_BROADCAST (pruned) to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
LOGINFO(6, "sending BLOCK_BROADCAST (" << (send_compact ? "compact" : "pruned") << ") to " << log::Gray() << static_cast<char*>(client->m_addrString));
|
||||
const std::vector<uint8_t>& blob = send_compact ? data->compact_blob : data->pruned_blob;
|
||||
|
||||
const uint32_t len = static_cast<uint32_t>(data->pruned_blob.size());
|
||||
const uint32_t len = static_cast<uint32_t>(blob.size());
|
||||
if (buf_size < SEND_BUF_MIN_SIZE + 1 + sizeof(uint32_t) + len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BROADCAST);
|
||||
*(p++) = static_cast<uint8_t>(send_compact ? MessageId::BLOCK_BROADCAST_COMPACT : MessageId::BLOCK_BROADCAST);
|
||||
|
||||
memcpy(p, &len, sizeof(uint32_t));
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
if (len) {
|
||||
memcpy(p, data->pruned_blob.data(), len);
|
||||
memcpy(p, blob.data(), len);
|
||||
p += len;
|
||||
}
|
||||
}
|
||||
@@ -895,7 +931,9 @@ void P2PServer::on_broadcast()
|
||||
|
||||
uint64_t P2PServer::get_random64()
|
||||
{
|
||||
MutexLock lock(m_rngLock);
|
||||
if (!server_event_loop_thread) {
|
||||
LOGERR(1, "get_random64() was called from another thread, this is not thread safe");
|
||||
}
|
||||
return m_rng();
|
||||
}
|
||||
|
||||
@@ -925,7 +963,18 @@ void P2PServer::show_peers()
|
||||
|
||||
for (P2PClient* client = static_cast<P2PClient*>(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast<P2PClient*>(client->m_next)) {
|
||||
if (client->m_listenPort >= 0) {
|
||||
LOGINFO(0, (client->m_isIncoming ? "I " : "O ") << client->m_pingTime << " ms\t" << static_cast<char*>(client->m_addrString));
|
||||
char buf[32];
|
||||
log::Stream s(buf);
|
||||
if (client->m_P2PoolVersion) {
|
||||
s << "\tv" << (client->m_P2PoolVersion >> 16) << '.' << (client->m_P2PoolVersion & 0xFFFF) << "\t\0";
|
||||
}
|
||||
else {
|
||||
s << "\t \t\0";
|
||||
}
|
||||
LOGINFO(0, (client->m_isIncoming ? "I\t" : "O\t")
|
||||
<< log::pad_right(client->m_pingTime, 4) << " ms\t"
|
||||
<< static_cast<const char*>(buf)
|
||||
<< static_cast<char*>(client->m_addrString));
|
||||
++n;
|
||||
}
|
||||
}
|
||||
@@ -939,7 +988,7 @@ int P2PServer::listen_port() const
|
||||
return params.m_p2pExternalPort ? params.m_p2pExternalPort : m_listenPort;
|
||||
}
|
||||
|
||||
int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size)
|
||||
int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size, bool compact)
|
||||
{
|
||||
int result;
|
||||
|
||||
@@ -948,7 +997,7 @@ int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size)
|
||||
result = m_blockDeserializeResult;
|
||||
}
|
||||
else {
|
||||
result = m_block->deserialize(buf, size, m_pool->side_chain(), &m_loop);
|
||||
result = m_block->deserialize(buf, size, m_pool->side_chain(), &m_loop, compact);
|
||||
m_blockDeserializeBuf.assign(buf, buf + size);
|
||||
m_blockDeserializeResult = result;
|
||||
m_lookForMissingBlocks = true;
|
||||
@@ -972,6 +1021,7 @@ void P2PServer::on_timer()
|
||||
save_peer_list_async();
|
||||
update_peer_connections();
|
||||
check_zmq();
|
||||
check_block_template();
|
||||
}
|
||||
|
||||
void P2PServer::flush_cache()
|
||||
@@ -993,13 +1043,13 @@ void P2PServer::flush_cache()
|
||||
const int err = uv_queue_work(&m_loop, &work->req,
|
||||
[](uv_work_t* req)
|
||||
{
|
||||
bkg_jobs_tracker.start("P2PServer::flush_cache");
|
||||
BACKGROUND_JOB_START(P2PServer::flush_cache);
|
||||
reinterpret_cast<Work*>(req->data)->cache->flush();
|
||||
},
|
||||
[](uv_work_t* req, int)
|
||||
{
|
||||
delete reinterpret_cast<Work*>(req->data);
|
||||
bkg_jobs_tracker.stop("P2PServer::flush_cache");
|
||||
BACKGROUND_JOB_STOP(P2PServer::flush_cache);
|
||||
});
|
||||
|
||||
if (err) {
|
||||
@@ -1106,6 +1156,19 @@ void P2PServer::check_zmq()
|
||||
}
|
||||
}
|
||||
|
||||
void P2PServer::check_block_template()
|
||||
{
|
||||
if (!m_pool->side_chain().precalcFinished()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Force update block template every 30 seconds after the initial sync is done
|
||||
if (seconds_since_epoch() >= m_pool->block_template().last_updated() + 30) {
|
||||
LOGINFO(4, "block template is 30 seconds old, updating it");
|
||||
m_pool->update_block_template_async();
|
||||
}
|
||||
}
|
||||
|
||||
P2PServer::P2PClient::P2PClient()
|
||||
: m_peerId(0)
|
||||
, m_expectedMessage(MessageId::HANDSHAKE_CHALLENGE)
|
||||
@@ -1119,6 +1182,8 @@ P2PServer::P2PClient::P2PClient()
|
||||
, m_nextOutgoingPeerListRequest(0)
|
||||
, m_lastPeerListRequestTime{}
|
||||
, m_peerListPendingRequests(0)
|
||||
, m_protocolVersion(PROTOCOL_VERSION_1_0)
|
||||
, m_P2PoolVersion(0)
|
||||
, m_pingTime(-1)
|
||||
, m_blockPendingRequests(0)
|
||||
, m_chainTipBlockRequest(false)
|
||||
@@ -1164,6 +1229,8 @@ void P2PServer::P2PClient::reset()
|
||||
m_nextOutgoingPeerListRequest = 0;
|
||||
m_lastPeerListRequestTime = {};
|
||||
m_peerListPendingRequests = 0;
|
||||
m_protocolVersion = PROTOCOL_VERSION_1_0;
|
||||
m_P2PoolVersion = 0;
|
||||
m_pingTime = -1;
|
||||
m_blockPendingRequests = 0;
|
||||
m_chainTipBlockRequest = false;
|
||||
@@ -1350,16 +1417,20 @@ bool P2PServer::P2PClient::on_read(char* data, uint32_t size)
|
||||
break;
|
||||
|
||||
case MessageId::BLOCK_BROADCAST:
|
||||
LOGINFO(6, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST");
|
||||
case MessageId::BLOCK_BROADCAST_COMPACT:
|
||||
{
|
||||
const bool compact = (id == MessageId::BLOCK_BROADCAST_COMPACT);
|
||||
LOGINFO(6, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor() << " sent " << (compact ? "BLOCK_BROADCAST_COMPACT" : "BLOCK_BROADCAST"));
|
||||
|
||||
if (bytes_left >= 1 + sizeof(uint32_t)) {
|
||||
const uint32_t block_size = read_unaligned(reinterpret_cast<uint32_t*>(buf + 1));
|
||||
if (bytes_left >= 1 + sizeof(uint32_t) + block_size) {
|
||||
bytes_read = 1 + sizeof(uint32_t) + block_size;
|
||||
if (!on_block_broadcast(buf + 1 + sizeof(uint32_t), block_size)) {
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
return false;
|
||||
if (bytes_left >= 1 + sizeof(uint32_t)) {
|
||||
const uint32_t block_size = read_unaligned(reinterpret_cast<uint32_t*>(buf + 1));
|
||||
if (bytes_left >= 1 + sizeof(uint32_t) + block_size) {
|
||||
bytes_read = 1 + sizeof(uint32_t) + block_size;
|
||||
if (!on_block_broadcast(buf + 1 + sizeof(uint32_t), block_size, compact)) {
|
||||
ban(DEFAULT_BAN_TIME);
|
||||
server->remove_peer_from_list(this);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1519,7 +1590,7 @@ void P2PServer::P2PClient::send_handshake_solution(const uint8_t (&challenge)[CH
|
||||
const int err = uv_queue_work(&server->m_loop, &work->req,
|
||||
[](uv_work_t* req)
|
||||
{
|
||||
bkg_jobs_tracker.start("P2PServer::send_handshake_solution");
|
||||
BACKGROUND_JOB_START(P2PServer::send_handshake_solution);
|
||||
|
||||
Work* work = reinterpret_cast<Work*>(req->data);
|
||||
const std::vector<uint8_t>& consensus_id = work->server->m_pool->side_chain().consensus_id();
|
||||
@@ -1576,7 +1647,7 @@ void P2PServer::P2PClient::send_handshake_solution(const uint8_t (&challenge)[CH
|
||||
[work]()
|
||||
{
|
||||
delete work;
|
||||
bkg_jobs_tracker.stop("P2PServer::send_handshake_solution");
|
||||
BACKGROUND_JOB_STOP(P2PServer::send_handshake_solution);
|
||||
});
|
||||
|
||||
// We might've been disconnected while working on the challenge, do nothing in this case
|
||||
@@ -1848,7 +1919,7 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size)
|
||||
|
||||
MutexLock lock(server->m_blockLock);
|
||||
|
||||
const int result = server->deserialize_block(buf, size);
|
||||
const int result = server->deserialize_block(buf, size, false);
|
||||
if (result != 0) {
|
||||
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
|
||||
return false;
|
||||
@@ -1865,13 +1936,19 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size)
|
||||
return false;
|
||||
}
|
||||
|
||||
server->send_peer_list_request(this, seconds_since_epoch());
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
if (cur_time >= m_nextOutgoingPeerListRequest) {
|
||||
server->send_peer_list_request(this, cur_time);
|
||||
}
|
||||
}
|
||||
|
||||
return handle_incoming_block_async(server->get_block());
|
||||
const SideChain& side_chain = server->m_pool->side_chain();
|
||||
const uint64_t max_time_delta = side_chain.precalcFinished() ? (side_chain.block_time() * side_chain.chain_window_size() * 4) : 0;
|
||||
|
||||
return handle_incoming_block_async(server->get_block(), max_time_delta);
|
||||
}
|
||||
|
||||
bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size, bool compact)
|
||||
{
|
||||
if (!size) {
|
||||
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " broadcasted an empty block");
|
||||
@@ -1882,7 +1959,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
|
||||
MutexLock lock(server->m_blockLock);
|
||||
|
||||
const int result = server->deserialize_block(buf, size);
|
||||
const int result = server->deserialize_block(buf, size, compact);
|
||||
if (result != 0) {
|
||||
LOGWARN(3, "peer " << static_cast<char*>(m_addrString) << " sent an invalid block, error " << result);
|
||||
return false;
|
||||
@@ -1903,13 +1980,10 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
if (our_height - peer_height < 5) {
|
||||
using namespace std::chrono;
|
||||
const int64_t elapsed_ms = duration_cast<milliseconds>(high_resolution_clock::now() - miner_data.time_received).count();
|
||||
if (our_height - peer_height > 1) {
|
||||
if ((our_height - peer_height > 1) || (elapsed_ms > 10000)) {
|
||||
LOGWARN(5, "peer " << static_cast<char*>(m_addrString) << " broadcasted a stale block (" << elapsed_ms << " ms late, mainchain height " << peer_height << ", expected >= " << our_height << "), ignoring it");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
LOGINFO(5, "peer " << static_cast<char*>(m_addrString) << " broadcasted a stale block (" << elapsed_ms << " ms late, mainchain height " << peer_height << ", expected >= " << our_height << ")");
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString) << " broadcasted an unreasonably stale block (mainchain height " << peer_height << ", expected >= " << our_height << ')');
|
||||
@@ -1930,13 +2004,14 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size)
|
||||
|
||||
m_lastBroadcastTimestamp = seconds_since_epoch();
|
||||
|
||||
return handle_incoming_block_async(block);
|
||||
return handle_incoming_block_async(block, 1800);
|
||||
}
|
||||
|
||||
bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
{
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
const bool first = (m_prevIncomingPeerListRequest == 0);
|
||||
|
||||
// Allow peer list requests no more than once every 30 seconds
|
||||
if (cur_time - m_prevIncomingPeerListRequest < 30) {
|
||||
@@ -1981,6 +2056,27 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
}
|
||||
}
|
||||
|
||||
// Protocol version message:
|
||||
// - IPv4 address = 255.255.255.255
|
||||
// - port = 65535
|
||||
// - first 12 bytes of the 16-byte raw IP address are ignored by older clients if it's IPv4
|
||||
// - use first 8 bytes of the 16-byte raw IP address to send supported protocol version and p2pool version
|
||||
if (first) {
|
||||
LOGINFO(5, "sending protocol version " << (SUPPORTED_PROTOCOL_VERSION >> 16) << '.' << (SUPPORTED_PROTOCOL_VERSION & 0xFFFF)
|
||||
<< ", P2Pool version " << P2POOL_VERSION_MAJOR << '.' << P2POOL_VERSION_MINOR
|
||||
<< " to peer " << log::Gray() << static_cast<char*>(m_addrString));
|
||||
|
||||
peers[0] = {};
|
||||
*reinterpret_cast<uint32_t*>(peers[0].m_addr.data) = SUPPORTED_PROTOCOL_VERSION;
|
||||
*reinterpret_cast<uint32_t*>(peers[0].m_addr.data + 4) = (P2POOL_VERSION_MAJOR << 16) | P2POOL_VERSION_MINOR;
|
||||
*reinterpret_cast<uint32_t*>(peers[0].m_addr.data + 12) = 0xFFFFFFFFU;
|
||||
peers[0].m_port = 0xFFFF;
|
||||
|
||||
if (num_selected_peers == 0) {
|
||||
num_selected_peers = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return server->send(this,
|
||||
[this, &peers, num_selected_peers](void* buf, size_t buf_size) -> size_t
|
||||
{
|
||||
@@ -2012,7 +2108,7 @@ bool P2PServer::P2PClient::on_peer_list_request(const uint8_t*)
|
||||
});
|
||||
}
|
||||
|
||||
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||
bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf)
|
||||
{
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
const uint64_t cur_time = seconds_since_epoch();
|
||||
@@ -2021,23 +2117,44 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||
|
||||
const uint32_t num_peers = *(buf++);
|
||||
for (uint32_t i = 0; i < num_peers; ++i) {
|
||||
const bool is_v6 = *(buf++) != 0;
|
||||
bool is_v6 = *(buf++) != 0;
|
||||
|
||||
raw_ip ip;
|
||||
memcpy(ip.data, buf, sizeof(ip.data));
|
||||
buf += sizeof(ip.data);
|
||||
|
||||
// Fill in default bytes for IPv4 addresses
|
||||
int port = 0;
|
||||
memcpy(&port, buf, 2);
|
||||
buf += 2;
|
||||
|
||||
// Treat IPv4-mapped addresses as regular IPv4 addresses
|
||||
if (is_v6 && ip.is_ipv4_prefix()) {
|
||||
is_v6 = false;
|
||||
}
|
||||
|
||||
if (!is_v6) {
|
||||
const uint32_t b = ip.data[12];
|
||||
if ((b == 0) || (b >= 224)) {
|
||||
// Ignore 0.0.0.0/8 (special-purpose range for "this network") and 224.0.0.0/3 (IP multicast and reserved ranges)
|
||||
|
||||
// Check for protocol version message
|
||||
if ((*reinterpret_cast<uint32_t*>(ip.data + 12) == 0xFFFFFFFFU) && (port == 0xFFFF)) {
|
||||
m_protocolVersion = *reinterpret_cast<uint32_t*>(ip.data);
|
||||
m_P2PoolVersion = *reinterpret_cast<uint32_t*>(ip.data + 4);
|
||||
LOGINFO(5, "peer " << log::Gray() << static_cast<char*>(m_addrString) << log::NoColor()
|
||||
<< " supports protocol version " << (m_protocolVersion >> 16) << '.' << (m_protocolVersion & 0xFFFF)
|
||||
<< ", runs P2Pool version " << (m_P2PoolVersion >> 16) << '.' << (m_P2PoolVersion & 0xFFFF)
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fill in default bytes for IPv4 addresses
|
||||
memset(ip.data, 0, 10);
|
||||
ip.data[10] = 0xFF;
|
||||
ip.data[11] = 0xFF;
|
||||
}
|
||||
|
||||
int port = 0;
|
||||
memcpy(&port, buf, 2);
|
||||
buf += 2;
|
||||
|
||||
bool already_added = false;
|
||||
for (Peer& p : server->m_peerList) {
|
||||
if ((p.m_isV6 == is_v6) && (p.m_addr == ip)) {
|
||||
@@ -2055,12 +2172,59 @@ bool P2PServer::P2PClient::on_peer_list_response(const uint8_t* buf) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool P2PServer::P2PClient::handle_incoming_block_async(const PoolBlock* block)
|
||||
bool P2PServer::P2PClient::handle_incoming_block_async(const PoolBlock* block, uint64_t max_time_delta)
|
||||
{
|
||||
P2PServer* server = static_cast<P2PServer*>(m_owner);
|
||||
SideChain& side_chain = server->m_pool->side_chain();
|
||||
|
||||
if (server->m_pool->side_chain().block_seen(*block)) {
|
||||
LOGINFO(6, "block " << block->m_sidechainId << " was received before, skipping it");
|
||||
// Limit system clock difference between connected peers
|
||||
// Check only new blocks (not added to side_chain yet)
|
||||
if (max_time_delta && !side_chain.find_block(block->m_sidechainId)) {
|
||||
static hash prev_checked_blocks[2];
|
||||
const bool is_new = (block->m_sidechainId != prev_checked_blocks[0]) && (block->m_sidechainId != prev_checked_blocks[1]);
|
||||
if (is_new) {
|
||||
prev_checked_blocks[1] = prev_checked_blocks[0];
|
||||
prev_checked_blocks[0] = block->m_sidechainId;
|
||||
}
|
||||
|
||||
const uint64_t t = time(nullptr);
|
||||
const uint32_t failed = ((block->m_timestamp + max_time_delta < t) || (block->m_timestamp > t + max_time_delta)) ? 1 : 0;
|
||||
|
||||
static uint32_t failed_history = 0;
|
||||
if (is_new) {
|
||||
failed_history = (failed_history << 1) | failed;
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
if (is_new) {
|
||||
int64_t dt = static_cast<int64_t>(block->m_timestamp - t);
|
||||
char sign = '+';
|
||||
if (dt < 0) {
|
||||
sign = '-';
|
||||
dt = -dt;
|
||||
}
|
||||
LOGWARN(4, "peer " << static_cast<char*>(m_addrString)
|
||||
<< " sent a block " << block->m_sidechainId << " (mined by " << block->m_minerWallet << ") with an invalid timestamp " << block->m_timestamp
|
||||
<< " (" << sign << dt << " seconds)");
|
||||
|
||||
uint32_t failed_checks = 0;
|
||||
|
||||
for (uint32_t k = 1; k != 0; k <<= 1) {
|
||||
if (failed_history & k) {
|
||||
++failed_checks;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed_checks > 16) {
|
||||
LOGWARN(1, "Your system clock might be invalid: " << failed_checks << " of 32 last blocks were rejected due to high timestamp diff");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (side_chain.block_seen(*block)) {
|
||||
LOGINFO(6, "block " << block->m_sidechainId << " (nonce " << block->m_nonce << ", extra_nonce " << block->m_extraNonce << ") was received before, skipping it");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2081,7 +2245,7 @@ bool P2PServer::P2PClient::handle_incoming_block_async(const PoolBlock* block)
|
||||
const int err = uv_queue_work(&server->m_loop, &work->req,
|
||||
[](uv_work_t* req)
|
||||
{
|
||||
bkg_jobs_tracker.start("P2PServer::handle_incoming_block_async");
|
||||
BACKGROUND_JOB_START(P2PServer::handle_incoming_block_async);
|
||||
Work* work = reinterpret_cast<Work*>(req->data);
|
||||
work->client->handle_incoming_block(work->server->m_pool, work->block, work->client_reset_counter, work->client_ip, work->missing_blocks);
|
||||
},
|
||||
@@ -2090,7 +2254,7 @@ bool P2PServer::P2PClient::handle_incoming_block_async(const PoolBlock* block)
|
||||
Work* work = reinterpret_cast<Work*>(req->data);
|
||||
work->client->post_handle_incoming_block(work->client_reset_counter, work->missing_blocks);
|
||||
delete work;
|
||||
bkg_jobs_tracker.stop("P2PServer::handle_incoming_block_async");
|
||||
BACKGROUND_JOB_STOP(P2PServer::handle_incoming_block_async);
|
||||
});
|
||||
|
||||
if (err != 0) {
|
||||
|
||||
+17
-6
@@ -30,6 +30,11 @@ static constexpr size_t PEER_LIST_RESPONSE_MAX_PEERS = 16;
|
||||
static constexpr int DEFAULT_P2P_PORT = 37889;
|
||||
static constexpr int DEFAULT_P2P_PORT_MINI = 37888;
|
||||
|
||||
static constexpr uint32_t PROTOCOL_VERSION_1_0 = 0x00010000UL;
|
||||
static constexpr uint32_t PROTOCOL_VERSION_1_1 = 0x00010001UL;
|
||||
|
||||
static constexpr uint32_t SUPPORTED_PROTOCOL_VERSION = PROTOCOL_VERSION_1_1;
|
||||
|
||||
class P2PServer : public TCPServer<P2P_BUF_SIZE, P2P_BUF_SIZE>
|
||||
{
|
||||
public:
|
||||
@@ -42,6 +47,7 @@ public:
|
||||
BLOCK_BROADCAST = 5,
|
||||
PEER_LIST_REQUEST = 6,
|
||||
PEER_LIST_RESPONSE = 7,
|
||||
BLOCK_BROADCAST_COMPACT = 8,
|
||||
};
|
||||
|
||||
explicit P2PServer(p2pool *pool);
|
||||
@@ -94,11 +100,11 @@ public:
|
||||
bool on_listen_port(const uint8_t* buf);
|
||||
bool on_block_request(const uint8_t* buf);
|
||||
bool on_block_response(const uint8_t* buf, uint32_t size);
|
||||
bool on_block_broadcast(const uint8_t* buf, uint32_t size);
|
||||
bool on_block_broadcast(const uint8_t* buf, uint32_t size, bool compact);
|
||||
bool on_peer_list_request(const uint8_t* buf);
|
||||
bool on_peer_list_response(const uint8_t* buf) const;
|
||||
bool on_peer_list_response(const uint8_t* buf);
|
||||
|
||||
bool handle_incoming_block_async(const PoolBlock* block);
|
||||
bool handle_incoming_block_async(const PoolBlock* block, uint64_t max_time_delta = 0);
|
||||
void handle_incoming_block(p2pool* pool, PoolBlock& block, const uint32_t reset_counter, const raw_ip& addr, std::vector<hash>& missing_blocks);
|
||||
void post_handle_incoming_block(const uint32_t reset_counter, std::vector<hash>& missing_blocks);
|
||||
|
||||
@@ -117,6 +123,10 @@ public:
|
||||
uint64_t m_nextOutgoingPeerListRequest;
|
||||
std::chrono::high_resolution_clock::time_point m_lastPeerListRequestTime;
|
||||
int m_peerListPendingRequests;
|
||||
|
||||
uint32_t m_protocolVersion;
|
||||
uint32_t m_P2PoolVersion;
|
||||
|
||||
int64_t m_pingTime;
|
||||
|
||||
int m_blockPendingRequests;
|
||||
@@ -130,7 +140,7 @@ public:
|
||||
std::atomic<uint32_t> m_broadcastedHashesIndex{ 0 };
|
||||
};
|
||||
|
||||
void broadcast(const PoolBlock& block);
|
||||
void broadcast(const PoolBlock& block, const PoolBlock* parent);
|
||||
uint64_t get_random64();
|
||||
uint64_t get_peerId() const { return m_peerId; }
|
||||
|
||||
@@ -146,7 +156,7 @@ public:
|
||||
void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 450U); }
|
||||
void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 450U); }
|
||||
|
||||
int deserialize_block(const uint8_t* buf, uint32_t size);
|
||||
int deserialize_block(const uint8_t* buf, uint32_t size, bool compact);
|
||||
const PoolBlock* get_block() const { return m_block; }
|
||||
|
||||
private:
|
||||
@@ -167,6 +177,7 @@ private:
|
||||
void flush_cache();
|
||||
void download_missing_blocks();
|
||||
void check_zmq();
|
||||
void check_block_template();
|
||||
void update_peer_connections();
|
||||
void update_peer_list();
|
||||
void send_peer_list_request(P2PClient* client, uint64_t cur_time);
|
||||
@@ -178,7 +189,6 @@ private:
|
||||
void remove_peer_from_list(P2PClient* client);
|
||||
void remove_peer_from_list(const raw_ip& ip);
|
||||
|
||||
uv_mutex_t m_rngLock;
|
||||
std::mt19937_64 m_rng;
|
||||
|
||||
uv_mutex_t m_blockLock;
|
||||
@@ -211,6 +221,7 @@ private:
|
||||
{
|
||||
std::vector<uint8_t> blob;
|
||||
std::vector<uint8_t> pruned_blob;
|
||||
std::vector<uint8_t> compact_blob;
|
||||
std::vector<hash> ancestor_hashes;
|
||||
};
|
||||
|
||||
|
||||
+8
-19
@@ -162,7 +162,7 @@ p2pool::p2pool(int argc, char* argv[])
|
||||
m_hasher = new RandomX_Hasher_RPC(this);
|
||||
#endif
|
||||
|
||||
m_blockTemplate = new BlockTemplate(this);
|
||||
m_blockTemplate = new BlockTemplate(m_sideChain, m_hasher);
|
||||
m_mempool = new Mempool();
|
||||
|
||||
try {
|
||||
@@ -677,14 +677,14 @@ void p2pool::download_block_headers(uint64_t current_height)
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGERR(1, "fatal error: couldn't download block header for height " << height);
|
||||
LOGERR(1, "fatal error: couldn't download block header for seed height " << height);
|
||||
panic();
|
||||
}
|
||||
},
|
||||
[height](const char* data, size_t size)
|
||||
{
|
||||
if (size > 0) {
|
||||
LOGERR(1, "fatal error: couldn't download block header for height " << height << ", error " << log::const_buf(data, size));
|
||||
LOGERR(1, "fatal error: couldn't download block header for seed height " << height << ", error " << log::const_buf(data, size));
|
||||
panic();
|
||||
}
|
||||
});
|
||||
@@ -720,15 +720,15 @@ void p2pool::download_block_headers(uint64_t current_height)
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1);
|
||||
panic();
|
||||
LOGERR(1, "Couldn't download block headers for heights " << start_height << " - " << current_height - 1);
|
||||
download_block_headers(current_height);
|
||||
}
|
||||
},
|
||||
[start_height, current_height](const char* data, size_t size)
|
||||
[this, start_height, current_height](const char* data, size_t size)
|
||||
{
|
||||
if (size > 0) {
|
||||
LOGERR(1, "fatal error: couldn't download block headers for heights " << start_height << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
|
||||
panic();
|
||||
LOGERR(1, "Couldn't download block headers for heights " << start_height << " - " << current_height - 1 << ", error " << log::const_buf(data, size));
|
||||
download_block_headers(current_height);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1442,12 +1442,6 @@ static void on_signal(uv_signal_t* handle, int signum)
|
||||
pool->stop();
|
||||
}
|
||||
|
||||
static bool init_uv_threadpool()
|
||||
{
|
||||
static uv_work_t dummy;
|
||||
return (uv_queue_work(uv_default_loop_checked(), &dummy, [](uv_work_t*) {}, nullptr) == 0);
|
||||
}
|
||||
|
||||
bool init_signals(p2pool* pool, bool init)
|
||||
{
|
||||
#ifdef SIGPIPE
|
||||
@@ -1532,11 +1526,6 @@ int p2pool::run()
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!init_uv_threadpool()) {
|
||||
LOGERR(1, "failed to start UV thread pool");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!init_signals(this, true)) {
|
||||
LOGERR(1, "failed to initialize signal handlers");
|
||||
return 1;
|
||||
|
||||
+3
-1
@@ -363,7 +363,9 @@ uint64_t PoolBlock::get_payout(const Wallet& w) const
|
||||
hash eph_public_key;
|
||||
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
if (w.get_eph_public_key_with_view_tag(m_txkeySec, i, eph_public_key, out.m_viewTag) && (eph_public_key == out.m_ephPublicKey)) {
|
||||
uint8_t view_tag;
|
||||
const uint8_t expected_view_tag = out.m_viewTag;
|
||||
if (w.get_eph_public_key(m_txkeySec, i, eph_public_key, view_tag, &expected_view_tag) && (eph_public_key == out.m_ephPublicKey)) {
|
||||
return out.m_reward;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-1
@@ -135,7 +135,7 @@ struct PoolBlock
|
||||
std::vector<uint8_t> serialize_mainchain_data_nolock(size_t* header_size, size_t* miner_tx_size, int* outputs_offset, int* outputs_blob_size) const;
|
||||
std::vector<uint8_t> serialize_sidechain_data() const;
|
||||
|
||||
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop);
|
||||
int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact);
|
||||
void reset_offchain_data();
|
||||
|
||||
bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash);
|
||||
@@ -145,6 +145,18 @@ struct PoolBlock
|
||||
// Both tx types are allowed by Monero consensus during v15 because it needs to process pre-fork mempool transactions,
|
||||
// but P2Pool can switch to using only TXOUT_TO_TAGGED_KEY for miner payouts starting from v15
|
||||
FORCEINLINE uint8_t get_tx_type() const { return (m_majorVersion < HARDFORK_VIEW_TAGS_VERSION) ? TXOUT_TO_KEY : TXOUT_TO_TAGGED_KEY; }
|
||||
|
||||
typedef std::array<uint8_t, HASH_SIZE + NONCE_SIZE + EXTRA_NONCE_SIZE> full_id;
|
||||
|
||||
FORCEINLINE full_id get_full_id() const
|
||||
{
|
||||
full_id key;
|
||||
uint8_t* p = key.data();
|
||||
memcpy(p, m_sidechainId.h, HASH_SIZE);
|
||||
memcpy(p + HASH_SIZE, &m_nonce, NONCE_SIZE);
|
||||
memcpy(p + HASH_SIZE + NONCE_SIZE, &m_extraNonce, EXTRA_NONCE_SIZE);
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace p2pool
|
||||
|
||||
+79
-20
@@ -23,7 +23,7 @@ namespace p2pool {
|
||||
// Since data here can come from external and possibly malicious sources, check everything
|
||||
// Only the syntax (i.e. the serialized block binary format) and the keccak hash are checked here
|
||||
// Semantics must also be checked elsewhere before accepting the block (PoW, reward split between miners, difficulty calculation and so on)
|
||||
int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop)
|
||||
int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact)
|
||||
{
|
||||
try {
|
||||
// Sanity check
|
||||
@@ -193,23 +193,60 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
|
||||
uint64_t num_transactions;
|
||||
READ_VARINT(num_transactions);
|
||||
|
||||
if (num_transactions > std::numeric_limits<uint64_t>::max() / HASH_SIZE) return __LINE__;
|
||||
if (static_cast<uint64_t>(data_end - data) < num_transactions * HASH_SIZE) return __LINE__;
|
||||
const int transactions_offset = static_cast<int>(data - data_begin);
|
||||
|
||||
m_transactions.resize(1);
|
||||
m_transactions.reserve(num_transactions + 1);
|
||||
std::vector<uint64_t> parent_indices;
|
||||
if (compact) {
|
||||
if (static_cast<uint64_t>(data_end - data) < num_transactions) return __LINE__;
|
||||
|
||||
for (uint64_t i = 0; i < num_transactions; ++i) {
|
||||
hash id;
|
||||
READ_BUF(id.h, HASH_SIZE);
|
||||
m_transactions.emplace_back(std::move(id));
|
||||
m_transactions.resize(1);
|
||||
parent_indices.resize(1);
|
||||
|
||||
// limit reserved memory size because we can't check "num_transactions" properly here
|
||||
const uint64_t k = std::min<uint64_t>(num_transactions + 1, 256);
|
||||
m_transactions.reserve(k);
|
||||
parent_indices.reserve(k);
|
||||
|
||||
for (uint64_t i = 0; i < num_transactions; ++i) {
|
||||
uint64_t parent_index;
|
||||
READ_VARINT(parent_index);
|
||||
|
||||
hash id;
|
||||
if (parent_index == 0) {
|
||||
READ_BUF(id.h, HASH_SIZE);
|
||||
}
|
||||
|
||||
m_transactions.emplace_back(id);
|
||||
parent_indices.emplace_back(parent_index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (num_transactions > std::numeric_limits<uint64_t>::max() / HASH_SIZE) return __LINE__;
|
||||
if (static_cast<uint64_t>(data_end - data) < num_transactions * HASH_SIZE) return __LINE__;
|
||||
|
||||
m_transactions.resize(1);
|
||||
m_transactions.reserve(num_transactions + 1);
|
||||
|
||||
for (uint64_t i = 0; i < num_transactions; ++i) {
|
||||
hash id;
|
||||
READ_BUF(id.h, HASH_SIZE);
|
||||
m_transactions.emplace_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
const int transactions_actual_blob_size = static_cast<int>(data - data_begin) - transactions_offset;
|
||||
const int transactions_blob_size = static_cast<int>(num_transactions) * HASH_SIZE;
|
||||
const int transactions_blob_size_diff = transactions_blob_size - transactions_actual_blob_size;
|
||||
|
||||
m_transactions.shrink_to_fit();
|
||||
|
||||
#if POOL_BLOCK_DEBUG
|
||||
m_mainChainDataDebug.reserve((data - data_begin) + outputs_blob_size_diff);
|
||||
m_mainChainDataDebug.reserve((data - data_begin) + outputs_blob_size_diff + transactions_blob_size_diff);
|
||||
m_mainChainDataDebug.assign(data_begin, data_begin + outputs_offset);
|
||||
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), outputs_blob_size, 0);
|
||||
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), data_begin + outputs_offset + outputs_actual_blob_size, data);
|
||||
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), data_begin + outputs_offset + outputs_actual_blob_size, data_begin + transactions_offset);
|
||||
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), transactions_blob_size, 0);
|
||||
m_mainChainDataDebug.insert(m_mainChainDataDebug.end(), data_begin + transactions_offset + transactions_actual_blob_size, data);
|
||||
|
||||
const uint8_t* sidechain_data_begin = data;
|
||||
#endif
|
||||
@@ -239,6 +276,23 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
|
||||
|
||||
READ_BUF(m_parent.h, HASH_SIZE);
|
||||
|
||||
if (compact) {
|
||||
const PoolBlock* parent = sidechain.find_block(m_parent);
|
||||
if (!parent) {
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
for (uint64_t i = 1, n = m_transactions.size(); i < n; ++i) {
|
||||
const uint64_t parent_index = parent_indices[i];
|
||||
if (parent_index) {
|
||||
if (parent_index >= parent->m_transactions.size()) {
|
||||
return __LINE__;
|
||||
}
|
||||
m_transactions[i] = parent->m_transactions[parent_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t num_uncles;
|
||||
READ_VARINT(num_uncles);
|
||||
|
||||
@@ -251,7 +305,7 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
|
||||
for (uint64_t i = 0; i < num_uncles; ++i) {
|
||||
hash id;
|
||||
READ_BUF(id.h, HASH_SIZE);
|
||||
m_uncles.emplace_back(std::move(id));
|
||||
m_uncles.emplace_back(id);
|
||||
}
|
||||
|
||||
READ_VARINT(m_sidechainHeight);
|
||||
@@ -279,14 +333,18 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
|
||||
return __LINE__;
|
||||
}
|
||||
|
||||
const uint8_t* transactions_blob = reinterpret_cast<uint8_t*>(m_transactions.data() + 1);
|
||||
|
||||
#if POOL_BLOCK_DEBUG
|
||||
memcpy(m_mainChainDataDebug.data() + outputs_offset, outputs_blob.data(), outputs_blob_size);
|
||||
memcpy(m_mainChainDataDebug.data() + transactions_offset + outputs_blob_size_diff, transactions_blob, transactions_blob_size);
|
||||
#endif
|
||||
|
||||
hash check;
|
||||
const std::vector<uint8_t>& consensus_id = sidechain.consensus_id();
|
||||
const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff + transactions_blob_size_diff);
|
||||
keccak_custom(
|
||||
[nonce_offset, extra_nonce_offset, sidechain_hash_offset, data_begin, data_end, &consensus_id, &outputs_blob, outputs_blob_size_diff, outputs_offset, outputs_blob_size](int offset) -> uint8_t
|
||||
[nonce_offset, extra_nonce_offset, sidechain_hash_offset, data_begin, data_size, &consensus_id, &outputs_blob, outputs_blob_size_diff, outputs_offset, outputs_blob_size, transactions_blob, transactions_blob_size_diff, transactions_offset, transactions_blob_size](int offset) -> uint8_t
|
||||
{
|
||||
uint32_t k = static_cast<uint32_t>(offset - nonce_offset);
|
||||
if (k < NONCE_SIZE) {
|
||||
@@ -303,24 +361,25 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& si
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int data_size = static_cast<int>((data_end - data_begin) + outputs_blob_size_diff);
|
||||
if (offset < data_size) {
|
||||
if (offset < outputs_offset) {
|
||||
return data_begin[offset];
|
||||
}
|
||||
else if (offset < outputs_offset + outputs_blob_size) {
|
||||
const int tmp = offset - outputs_offset;
|
||||
return outputs_blob[tmp];
|
||||
return outputs_blob[offset - outputs_offset];
|
||||
}
|
||||
else {
|
||||
else if (offset < transactions_offset + outputs_blob_size_diff) {
|
||||
return data_begin[offset - outputs_blob_size_diff];
|
||||
}
|
||||
else if (offset < transactions_offset + outputs_blob_size_diff + transactions_blob_size) {
|
||||
return transactions_blob[offset - (transactions_offset + outputs_blob_size_diff)];
|
||||
}
|
||||
return data_begin[offset - outputs_blob_size_diff - transactions_blob_size_diff];
|
||||
}
|
||||
offset -= data_size;
|
||||
|
||||
return consensus_id[offset];
|
||||
return consensus_id[offset - data_size];
|
||||
},
|
||||
static_cast<int>(size + outputs_blob_size_diff + consensus_id.size()), check.h, HASH_SIZE);
|
||||
static_cast<int>(size + outputs_blob_size_diff + transactions_blob_size_diff + consensus_id.size()), check.h, HASH_SIZE);
|
||||
|
||||
if (check != m_sidechainId) {
|
||||
return __LINE__;
|
||||
|
||||
+4
-4
@@ -141,7 +141,7 @@ void RandomX_Hasher::set_seed_async(const hash& seed)
|
||||
const int err = uv_queue_work(uv_default_loop_checked(), &work->req,
|
||||
[](uv_work_t* req)
|
||||
{
|
||||
bkg_jobs_tracker.start("RandomX_Hasher::set_seed_async");
|
||||
BACKGROUND_JOB_START(RandomX_Hasher::set_seed_async);
|
||||
Work* work = reinterpret_cast<Work*>(req->data);
|
||||
if (!work->pool->stopped()) {
|
||||
work->hasher->set_seed(work->seed);
|
||||
@@ -150,7 +150,7 @@ void RandomX_Hasher::set_seed_async(const hash& seed)
|
||||
[](uv_work_t* req, int)
|
||||
{
|
||||
delete reinterpret_cast<Work*>(req->data);
|
||||
bkg_jobs_tracker.stop("RandomX_Hasher::set_seed_async");
|
||||
BACKGROUND_JOB_STOP(RandomX_Hasher::set_seed_async);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -382,8 +382,8 @@ RandomX_Hasher_RPC::RandomX_Hasher_RPC(p2pool* pool)
|
||||
// Init loop user data before running it
|
||||
GetLoopUserData(&m_loop);
|
||||
|
||||
uv_async_init(&m_loop, &m_shutdownAsync, on_shutdown);
|
||||
uv_async_init(&m_loop, &m_kickTheLoopAsync, nullptr);
|
||||
uv_async_init_checked(&m_loop, &m_shutdownAsync, on_shutdown);
|
||||
uv_async_init_checked(&m_loop, &m_kickTheLoopAsync, nullptr);
|
||||
m_shutdownAsync.data = this;
|
||||
|
||||
uv_mutex_init_checked(&m_requestMutex);
|
||||
|
||||
+95
-83
@@ -325,7 +325,7 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares
|
||||
uint64_t block_depth = 0;
|
||||
const PoolBlock* cur = tip;
|
||||
do {
|
||||
MinerShare cur_share{ cur->m_difficulty.lo, &cur->m_minerWallet };
|
||||
MinerShare cur_share{ cur->m_difficulty, &cur->m_minerWallet };
|
||||
|
||||
for (const hash& uncle_id : cur->m_uncles) {
|
||||
auto it = m_blocksById.find(uncle_id);
|
||||
@@ -343,14 +343,9 @@ bool SideChain::get_shares(const PoolBlock* tip, std::vector<MinerShare>& shares
|
||||
}
|
||||
|
||||
// Take some % of uncle's weight into this share
|
||||
uint64_t product[2];
|
||||
product[0] = umul128(uncle->m_difficulty.lo, m_unclePenalty, &product[1]);
|
||||
|
||||
uint64_t rem;
|
||||
const uint64_t uncle_penalty = udiv128(product[1], product[0], 100, &rem);
|
||||
|
||||
const difficulty_type uncle_penalty = uncle->m_difficulty * m_unclePenalty / 100;
|
||||
cur_share.m_weight += uncle_penalty;
|
||||
shares.emplace_back(uncle->m_difficulty.lo - uncle_penalty, &uncle->m_minerWallet);
|
||||
shares.emplace_back(uncle->m_difficulty - uncle_penalty, &uncle->m_minerWallet);
|
||||
}
|
||||
|
||||
shares.push_back(cur_share);
|
||||
@@ -453,19 +448,19 @@ bool SideChain::block_seen(const PoolBlock& block)
|
||||
|
||||
// Check if it was received before
|
||||
MutexLock lock(m_seenBlocksLock);
|
||||
return !m_seenBlocks.insert(block.m_sidechainId).second;
|
||||
return !m_seenBlocks.insert(block.get_full_id()).second;
|
||||
}
|
||||
|
||||
void SideChain::unsee_block(const PoolBlock& block)
|
||||
{
|
||||
MutexLock lock(m_seenBlocksLock);
|
||||
m_seenBlocks.erase(block.m_sidechainId);
|
||||
m_seenBlocks.erase(block.get_full_id());
|
||||
}
|
||||
|
||||
bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_blocks)
|
||||
{
|
||||
if (block.m_difficulty < m_minDifficulty) {
|
||||
LOGWARN(3, "add_external_block: block has invalid difficulty " << block.m_difficulty << ", expected >= " << m_minDifficulty);
|
||||
LOGWARN(3, "add_external_block: block mined by " << block.m_minerWallet << " has invalid difficulty " << block.m_difficulty << ", expected >= " << m_minDifficulty);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -498,7 +493,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
LOGINFO(4, "add_external_block: height = " << block.m_sidechainHeight << ", id = " << block.m_sidechainId << ", mainchain height = " << block.m_txinGenHeight);
|
||||
|
||||
if (too_low_diff) {
|
||||
LOGWARN(4, "add_external_block: block has too low difficulty " << block.m_difficulty << ", expected >= ~" << expected_diff << ". Ignoring it.");
|
||||
LOGWARN(4, "add_external_block: block mined by " << block.m_minerWallet << " has too low difficulty " << block.m_difficulty << ", expected >= ~" << expected_diff << ". Ignoring it.");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -506,7 +501,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
ChainMain data;
|
||||
if (m_pool->chainmain_get_by_hash(block.m_prevId, data)) {
|
||||
if (data.height + 1 != block.m_txinGenHeight) {
|
||||
LOGWARN(3, "add_external_block: wrong mainchain height " << block.m_txinGenHeight << ", expected " << data.height + 1);
|
||||
LOGWARN(3, "add_external_block mined by " << block.m_minerWallet << ": wrong mainchain height " << block.m_txinGenHeight << ", expected " << data.height + 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -516,7 +511,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
|
||||
hash seed;
|
||||
if (!m_pool->get_seed(block.m_txinGenHeight, seed)) {
|
||||
LOGWARN(3, "add_external_block: couldn't get seed hash for mainchain height " << block.m_txinGenHeight);
|
||||
LOGWARN(3, "add_external_block mined by " << block.m_minerWallet << ": couldn't get seed hash for mainchain height " << block.m_txinGenHeight);
|
||||
unsee_block(block);
|
||||
return false;
|
||||
}
|
||||
@@ -546,7 +541,7 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
|
||||
}
|
||||
|
||||
if (!block.m_difficulty.check_pow(pow_hash)) {
|
||||
LOGWARN(3, "add_external_block: not enough PoW for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight);
|
||||
LOGWARN(3, "add_external_block mined by " << block.m_minerWallet << ": not enough PoW for height = " << block.m_sidechainHeight << ", mainchain height " << block.m_txinGenHeight);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -604,10 +599,20 @@ void SideChain::add_block(const PoolBlock& block)
|
||||
|
||||
auto result = m_blocksById.insert({ new_block->m_sidechainId, new_block });
|
||||
if (!result.second) {
|
||||
LOGWARN(3, "add_block: trying to add the same block twice, id = "
|
||||
<< new_block->m_sidechainId << ", sidechain height = "
|
||||
<< new_block->m_sidechainHeight << ", height = "
|
||||
<< new_block->m_txinGenHeight);
|
||||
const PoolBlock* old_block = result.first->second;
|
||||
|
||||
LOGWARN(3, "add_block: trying to add the same block twice:"
|
||||
<< "\nnew block id = " << new_block->m_sidechainId
|
||||
<< ", sidechain height = " << new_block->m_sidechainHeight
|
||||
<< ", height = " << new_block->m_txinGenHeight
|
||||
<< ", nonce = " << new_block->m_nonce
|
||||
<< ", extra_nonce = " << new_block->m_extraNonce
|
||||
<< "\nold block id = " << old_block->m_sidechainId
|
||||
<< ", sidechain height = " << old_block->m_sidechainHeight
|
||||
<< ", height = " << old_block->m_txinGenHeight
|
||||
<< ", nonce = " << old_block->m_nonce
|
||||
<< ", extra_nonce = " << old_block->m_extraNonce
|
||||
);
|
||||
|
||||
delete new_block;
|
||||
return;
|
||||
@@ -691,37 +696,38 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
||||
{
|
||||
blob.clear();
|
||||
|
||||
ReadLock lock(m_sidechainLock);
|
||||
|
||||
auto it = m_blocksById.find(block->m_sidechainId);
|
||||
if (it != m_blocksById.end()) {
|
||||
PoolBlock* b = it->second;
|
||||
const size_t n = b->m_outputs.size();
|
||||
|
||||
blob.reserve(n * 39 + 64);
|
||||
writeVarint(n, blob);
|
||||
|
||||
const uint8_t tx_type = b->get_tx_type();
|
||||
|
||||
for (const PoolBlock::TxOutput& output : b->m_outputs) {
|
||||
writeVarint(output.m_reward, blob);
|
||||
blob.emplace_back(tx_type);
|
||||
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
|
||||
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
blob.emplace_back(static_cast<uint8_t>(output.m_viewTag));
|
||||
}
|
||||
}
|
||||
|
||||
block->m_outputs = b->m_outputs;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<MinerShare> tmpShares;
|
||||
std::vector<uint64_t> tmpRewards;
|
||||
{
|
||||
ReadLock lock(m_sidechainLock);
|
||||
|
||||
if (!get_shares(block, tmpShares) || !split_reward(total_reward, tmpShares, tmpRewards) || (tmpRewards.size() != tmpShares.size())) {
|
||||
return false;
|
||||
auto it = m_blocksById.find(block->m_sidechainId);
|
||||
if (it != m_blocksById.end()) {
|
||||
PoolBlock* b = it->second;
|
||||
const size_t n = b->m_outputs.size();
|
||||
|
||||
blob.reserve(n * 39 + 64);
|
||||
writeVarint(n, blob);
|
||||
|
||||
const uint8_t tx_type = b->get_tx_type();
|
||||
|
||||
for (const PoolBlock::TxOutput& output : b->m_outputs) {
|
||||
writeVarint(output.m_reward, blob);
|
||||
blob.emplace_back(tx_type);
|
||||
blob.insert(blob.end(), output.m_ephPublicKey.h, output.m_ephPublicKey.h + HASH_SIZE);
|
||||
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
blob.emplace_back(static_cast<uint8_t>(output.m_viewTag));
|
||||
}
|
||||
}
|
||||
|
||||
block->m_outputs = b->m_outputs;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!get_shares(block, tmpShares) || !split_reward(total_reward, tmpShares, tmpRewards) || (tmpRewards.size() != tmpShares.size())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t n = tmpShares.size();
|
||||
@@ -734,7 +740,17 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
||||
int num_helper_jobs_started = 0;
|
||||
|
||||
if (loop) {
|
||||
constexpr size_t HELPER_JOBS_COUNT = 4;
|
||||
uint32_t HELPER_JOBS_COUNT = std::thread::hardware_concurrency();
|
||||
|
||||
// this thread will also be running, so reduce helper job count by 1
|
||||
if (HELPER_JOBS_COUNT > 0) {
|
||||
--HELPER_JOBS_COUNT;
|
||||
}
|
||||
|
||||
// No more than 8 helper jobs because our UV worker thread pool has 8 threads
|
||||
if (HELPER_JOBS_COUNT > 8) {
|
||||
HELPER_JOBS_COUNT = 8;
|
||||
}
|
||||
|
||||
struct Work
|
||||
{
|
||||
@@ -765,7 +781,9 @@ bool SideChain::get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::v
|
||||
int index;
|
||||
while ((index = work->counter.fetch_sub(1)) >= 0) {
|
||||
uint8_t view_tag;
|
||||
work->tmpShares[index].m_wallet->get_eph_public_key(work->txkeySec, static_cast<size_t>(index), eph_public_key, view_tag);
|
||||
if (!work->tmpShares[index].m_wallet->get_eph_public_key(work->txkeySec, static_cast<size_t>(index), eph_public_key, view_tag)) {
|
||||
LOGWARN(6, "get_eph_public_key failed at index " << index);
|
||||
}
|
||||
}
|
||||
|
||||
++work->num_helper_jobs_finished;
|
||||
@@ -927,7 +945,9 @@ void SideChain::print_status(bool obtain_sidechain_lock) const
|
||||
const PoolBlock::TxOutput& out = tip->m_outputs[i];
|
||||
if (!your_reward) {
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
|
||||
uint8_t view_tag;
|
||||
const uint8_t expected_view_tag = out.m_viewTag;
|
||||
if (w.get_eph_public_key(tip->m_txkeySec, i, eph_public_key, view_tag, &expected_view_tag) && (out.m_ephPublicKey == eph_public_key)) {
|
||||
your_reward = out.m_reward;
|
||||
}
|
||||
}
|
||||
@@ -978,6 +998,7 @@ void SideChain::print_status(bool obtain_sidechain_lock) const
|
||||
"\nSide chain hashrate = " << log::Hashrate(pool_hashrate) <<
|
||||
(hashrate_est ? "\nYour hashrate (pool-side) = " : "") << (hashrate_est ? log::Hashrate(hashrate_est) : log::Hashrate()) <<
|
||||
"\nPPLNS window = " << total_blocks_in_window << " blocks (+" << total_uncles_in_window << " uncles, " << total_orphans << " orphans)" <<
|
||||
"\nYour wallet address = " << m_pool->params().m_wallet <<
|
||||
"\nYour shares = " << our_blocks_in_window_total << " blocks (+" << our_uncles_in_window_total << " uncles, " << our_orphans << " orphans)"
|
||||
<< our_blocks_in_window_chart << our_uncles_in_window_chart <<
|
||||
"\nBlock reward share = " << block_share << "% (" << log::XMRAmount(your_reward) << ')'
|
||||
@@ -999,7 +1020,9 @@ double SideChain::get_reward_share(const Wallet& w) const
|
||||
const PoolBlock::TxOutput& out = tip->m_outputs[i];
|
||||
if (!reward) {
|
||||
if (tx_type == TXOUT_TO_TAGGED_KEY) {
|
||||
if (w.get_eph_public_key_with_view_tag(tip->m_txkeySec, i, eph_public_key, out.m_viewTag) && (out.m_ephPublicKey == eph_public_key)) {
|
||||
uint8_t view_tag;
|
||||
const uint8_t expected_view_tag = out.m_viewTag;
|
||||
if (w.get_eph_public_key(tip->m_txkeySec, i, eph_public_key, view_tag, &expected_view_tag) && (out.m_ephPublicKey == eph_public_key)) {
|
||||
reward = out.m_reward;
|
||||
}
|
||||
}
|
||||
@@ -1065,9 +1088,9 @@ bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& sha
|
||||
{
|
||||
const size_t num_shares = shares.size();
|
||||
|
||||
const uint64_t total_weight = std::accumulate(shares.begin(), shares.end(), 0ULL, [](uint64_t a, const MinerShare& b) { return a + b.m_weight; });
|
||||
const difficulty_type total_weight = std::accumulate(shares.begin(), shares.end(), difficulty_type(), [](const difficulty_type& a, const MinerShare& b) { return a + b.m_weight; });
|
||||
|
||||
if (total_weight == 0) {
|
||||
if (total_weight.empty()) {
|
||||
LOGERR(1, "total_weight is 0. Check the code!");
|
||||
return false;
|
||||
}
|
||||
@@ -1076,18 +1099,14 @@ bool SideChain::split_reward(uint64_t reward, const std::vector<MinerShare>& sha
|
||||
rewards.reserve(num_shares);
|
||||
|
||||
// Each miner gets a proportional fraction of the block reward
|
||||
uint64_t w = 0;
|
||||
difficulty_type w;
|
||||
uint64_t reward_given = 0;
|
||||
for (uint64_t i = 0; i < num_shares; ++i) {
|
||||
w += shares[i].m_weight;
|
||||
|
||||
uint64_t hi;
|
||||
const uint64_t lo = umul128(w, reward, &hi);
|
||||
|
||||
uint64_t rem;
|
||||
const uint64_t next_value = udiv128(hi, lo, total_weight, &rem);
|
||||
rewards.emplace_back(next_value - reward_given);
|
||||
reward_given = next_value;
|
||||
const difficulty_type next_value = w * reward / total_weight;
|
||||
rewards.emplace_back(next_value.lo - reward_given);
|
||||
reward_given = next_value.lo;
|
||||
}
|
||||
|
||||
// Double check that we gave out the exact amount
|
||||
@@ -1182,20 +1201,7 @@ bool SideChain::get_difficulty(const PoolBlock* tip, std::vector<DifficultyData>
|
||||
}
|
||||
}
|
||||
|
||||
// This is correct as long as the difference between two 128-bit difficulties is less than 2^64, even if it wraps
|
||||
const uint64_t delta_diff = diff2.lo - diff1.lo;
|
||||
|
||||
uint64_t product[2];
|
||||
product[0] = umul128(delta_diff, m_targetBlockTime, &product[1]);
|
||||
|
||||
if (product[1] >= delta_t) {
|
||||
LOGERR(1, "calculated difficulty is too high for block at height = " << tip->m_sidechainHeight << ", id = " << tip->m_sidechainId << ", mainchain height = " << tip->m_txinGenHeight);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t rem;
|
||||
curDifficulty.lo = udiv128(product[1], product[0], delta_t, &rem);
|
||||
curDifficulty.hi = 0;
|
||||
curDifficulty = (diff2 - diff1) * m_targetBlockTime / delta_t;
|
||||
|
||||
if (curDifficulty < m_minDifficulty) {
|
||||
curDifficulty = m_minDifficulty;
|
||||
@@ -1231,7 +1237,7 @@ void SideChain::verify_loop(PoolBlock* block)
|
||||
if (block->m_invalid) {
|
||||
LOGWARN(3, "block at height = " << block->m_sidechainHeight <<
|
||||
", id = " << block->m_sidechainId <<
|
||||
", mainchain height = " << block->m_txinGenHeight << " is invalid");
|
||||
", mainchain height = " << block->m_txinGenHeight << ", mined by " << block->m_minerWallet << " is invalid");
|
||||
}
|
||||
else {
|
||||
LOGINFO(3, "verified block at height = " << block->m_sidechainHeight <<
|
||||
@@ -1258,7 +1264,10 @@ void SideChain::verify_loop(PoolBlock* block)
|
||||
if (block->m_wantBroadcast && !block->m_broadcasted) {
|
||||
block->m_broadcasted = true;
|
||||
if (server && (block->m_depth < UNCLE_BLOCK_DEPTH)) {
|
||||
server->broadcast(*block);
|
||||
if (m_pool && (block->m_minerWallet == m_pool->params().m_wallet)) {
|
||||
LOGINFO(0, log::Green() << "SHARE ADDED: height = " << block->m_sidechainHeight << ", id = " << block->m_sidechainId << ", mainchain height = " << block->m_txinGenHeight);
|
||||
}
|
||||
server->broadcast(*block, get_parent(block));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1657,7 +1666,7 @@ void SideChain::update_chain_tip(const PoolBlock* block)
|
||||
|
||||
if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) {
|
||||
block->m_broadcasted = true;
|
||||
p2pServer()->broadcast(*block);
|
||||
p2pServer()->broadcast(*block, get_parent(block));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2133,6 +2142,9 @@ void SideChain::finish_precalc()
|
||||
uv_mutex_destroy(&m_precalcJobsMutex);
|
||||
uv_cond_destroy(&m_precalcJobsCond);
|
||||
|
||||
// Also clear cache because it has data from all old blocks now
|
||||
clear_crypto_cache();
|
||||
|
||||
LOGINFO(4, "pre-calculation workers stopped");
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
@@ -2140,15 +2152,15 @@ void SideChain::finish_precalc()
|
||||
LOGERR(1, "exception in finish_precalc(): " << e.what());
|
||||
}
|
||||
|
||||
// Also clear cache because it has data from all old blocks now
|
||||
clear_crypto_cache();
|
||||
|
||||
#ifdef DEV_TEST_SYNC
|
||||
if (m_pool) {
|
||||
LOGINFO(0, log::LightGreen() << "[DEV] Synchronization finished successfully, stopping P2Pool now");
|
||||
print_status(false);
|
||||
if (m_pool->p2p_server()) {
|
||||
m_pool->p2p_server()->print_status();
|
||||
P2PServer* server = m_pool->p2p_server();
|
||||
if (server) {
|
||||
server->print_status();
|
||||
server->print_bans();
|
||||
server->show_peers_async();
|
||||
}
|
||||
m_pool->stop();
|
||||
}
|
||||
|
||||
+5
-7
@@ -18,6 +18,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "uv_util.h"
|
||||
#include "pool_block.h"
|
||||
#include <map>
|
||||
#include <thread>
|
||||
|
||||
@@ -25,16 +26,13 @@ namespace p2pool {
|
||||
|
||||
class p2pool;
|
||||
class P2PServer;
|
||||
struct DifficultyData;
|
||||
struct PoolBlock;
|
||||
class Wallet;
|
||||
|
||||
struct MinerShare
|
||||
{
|
||||
FORCEINLINE MinerShare() : m_weight(0), m_wallet(nullptr) {}
|
||||
FORCEINLINE MinerShare(uint64_t w, const Wallet* x) : m_weight(w), m_wallet(x) {}
|
||||
FORCEINLINE MinerShare() : m_weight(), m_wallet(nullptr) {}
|
||||
FORCEINLINE MinerShare(const difficulty_type& w, const Wallet* x) : m_weight(w), m_wallet(x) {}
|
||||
|
||||
uint64_t m_weight;
|
||||
difficulty_type m_weight;
|
||||
const Wallet* m_wallet;
|
||||
};
|
||||
|
||||
@@ -112,7 +110,7 @@ private:
|
||||
uint64_t m_seenWalletsLastPruneTime;
|
||||
|
||||
uv_mutex_t m_seenBlocksLock;
|
||||
unordered_set<hash> m_seenBlocks;
|
||||
unordered_set<PoolBlock::full_id> m_seenBlocks;
|
||||
|
||||
std::vector<DifficultyData> m_difficultyData;
|
||||
|
||||
|
||||
@@ -242,14 +242,14 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
|
||||
const uint32_t extra_nonce = m_extraNonce.fetch_add(1);
|
||||
|
||||
uint8_t hashing_blob[128];
|
||||
uint64_t height;
|
||||
uint64_t height, sidechain_height;
|
||||
difficulty_type difficulty;
|
||||
difficulty_type sidechain_difficulty;
|
||||
hash seed_hash;
|
||||
size_t nonce_offset;
|
||||
uint32_t template_id;
|
||||
|
||||
const size_t blob_size = m_pool->block_template().get_hashing_blob(extra_nonce, hashing_blob, height, difficulty, sidechain_difficulty, seed_hash, nonce_offset, template_id);
|
||||
const size_t blob_size = m_pool->block_template().get_hashing_blob(extra_nonce, hashing_blob, height, sidechain_height, difficulty, sidechain_difficulty, seed_hash, nonce_offset, template_id);
|
||||
|
||||
uint64_t target = std::max(difficulty.target(), sidechain_difficulty.target());
|
||||
|
||||
@@ -363,10 +363,10 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
|
||||
if (found) {
|
||||
BlockTemplate& block = m_pool->block_template();
|
||||
uint64_t height;
|
||||
uint64_t height, sidechain_height;
|
||||
difficulty_type mainchain_diff, sidechain_diff;
|
||||
|
||||
if (!block.get_difficulties(template_id, height, mainchain_diff, sidechain_diff)) {
|
||||
if (!block.get_difficulties(template_id, height, sidechain_height, mainchain_diff, sidechain_diff)) {
|
||||
LOGWARN(4, "client " << static_cast<char*>(client->m_addrString) << " got a stale share");
|
||||
return send(client,
|
||||
[id](void* buf, size_t buf_size)
|
||||
@@ -379,7 +379,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
|
||||
if (mainchain_diff.check_pow(resultHash)) {
|
||||
const char* s = client->m_customUser;
|
||||
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << " found a mainchain block, submitting it");
|
||||
LOGINFO(0, log::Green() << "client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << " found a mainchain block at height " << height << ", submitting it");
|
||||
m_pool->submit_block_async(template_id, nonce, extra_nonce);
|
||||
}
|
||||
|
||||
@@ -412,6 +412,7 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
|
||||
share->m_resultHash = resultHash;
|
||||
share->m_sidechainDifficulty = sidechain_diff;
|
||||
share->m_mainchainHeight = height;
|
||||
share->m_sidechainHeight = sidechain_height;
|
||||
share->m_effort = -1.0;
|
||||
share->m_timestamp = seconds_since_epoch();
|
||||
|
||||
@@ -806,7 +807,7 @@ void StratumServer::on_share_found(uv_work_t* req)
|
||||
{
|
||||
SubmittedShare* share = reinterpret_cast<SubmittedShare*>(req->data);
|
||||
if (share->m_highEnoughDifficulty) {
|
||||
bkg_jobs_tracker.start("StratumServer::on_share_found");
|
||||
BACKGROUND_JOB_START(StratumServer::on_share_found);
|
||||
}
|
||||
|
||||
StratumClient* client = share->m_client;
|
||||
@@ -890,8 +891,8 @@ void StratumServer::on_after_share_found(uv_work_t* req, int /*status*/)
|
||||
|
||||
if (share->m_highEnoughDifficulty) {
|
||||
const char* s = client->m_customUser;
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << share->m_mainchainHeight << ", diff " << share->m_sidechainDifficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << ", effort " << share->m_effort << '%');
|
||||
bkg_jobs_tracker.stop("StratumServer::on_share_found");
|
||||
LOGINFO(0, log::Green() << "SHARE FOUND: mainchain height " << share->m_mainchainHeight << ", sidechain height " << share->m_sidechainHeight << ", diff " << share->m_sidechainDifficulty << ", client " << static_cast<char*>(client->m_addrString) << (*s ? " user " : "") << s << ", effort " << share->m_effort << '%');
|
||||
BACKGROUND_JOB_STOP(StratumServer::on_share_found);
|
||||
}
|
||||
|
||||
ON_SCOPE_LEAVE([share]() { share->m_server->m_submittedSharesPool.push_back(share); });
|
||||
|
||||
@@ -141,6 +141,7 @@ private:
|
||||
hash m_resultHash;
|
||||
difficulty_type m_sidechainDifficulty;
|
||||
uint64_t m_mainchainHeight;
|
||||
uint64_t m_sidechainHeight;
|
||||
double m_effort;
|
||||
uint64_t m_timestamp;
|
||||
uint64_t m_hashes;
|
||||
|
||||
+77
-21
@@ -32,7 +32,7 @@ namespace p2pool {
|
||||
#define STR2(X) STR(X)
|
||||
#define STR(X) #X
|
||||
|
||||
const char* VERSION = "v2.5 (built"
|
||||
const char* VERSION = "v" STR2(P2POOL_VERSION_MAJOR) "." STR2(P2POOL_VERSION_MINOR) " (built"
|
||||
#if defined(__clang__)
|
||||
" with clang/" __clang_version__
|
||||
#elif defined(__GNUC__)
|
||||
@@ -75,6 +75,54 @@ void make_thread_background()
|
||||
#endif
|
||||
}
|
||||
|
||||
NOINLINE difficulty_type& difficulty_type::operator/=(difficulty_type b)
|
||||
{
|
||||
if (*this < b) {
|
||||
lo = 0;
|
||||
hi = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (*this - b < b) {
|
||||
lo = 1;
|
||||
hi = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (b.hi == 0) {
|
||||
return operator/=(b.lo);
|
||||
}
|
||||
|
||||
const uint64_t shift = bsr(b.hi) + 1;
|
||||
const uint64_t divisor = shiftleft128(b.lo, b.hi, 64 - shift);
|
||||
|
||||
uint64_t t;
|
||||
if (hi < divisor) {
|
||||
uint64_t r;
|
||||
t = udiv128(hi, lo, divisor, &r) >> shift;
|
||||
}
|
||||
else {
|
||||
uint64_t r;
|
||||
t = shiftright128(udiv128(hi - divisor, lo, divisor, &r), 1, shift);
|
||||
}
|
||||
|
||||
difficulty_type product;
|
||||
product.lo = umul128(b.lo, t, &product.hi);
|
||||
|
||||
uint64_t t1, t2;
|
||||
t1 = umul128(b.hi, t, &t2);
|
||||
product.hi += t1;
|
||||
|
||||
if (t2 || (product.hi < t1) || (*this < product)) {
|
||||
--t;
|
||||
}
|
||||
|
||||
lo = t;
|
||||
hi = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NOINLINE bool difficulty_type::check_pow(const hash& pow_hash) const
|
||||
{
|
||||
const uint64_t* a = reinterpret_cast<const uint64_t*>(pow_hash.h);
|
||||
@@ -126,13 +174,6 @@ NOINLINE bool difficulty_type::check_pow(const hash& pow_hash) const
|
||||
return true;
|
||||
}
|
||||
|
||||
difficulty_type operator+(const difficulty_type& a, const difficulty_type& b)
|
||||
{
|
||||
difficulty_type result = a;
|
||||
result += b;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& s, const difficulty_type& d)
|
||||
{
|
||||
char buf[log::Stream::BUF_SIZE + 1];
|
||||
@@ -197,6 +238,9 @@ std::istream& operator>>(std::istream& s, hash& h)
|
||||
found_number = true;
|
||||
h.h[index >> 1] = (h.h[index >> 1] << 4) | digit;
|
||||
++index;
|
||||
if (index >= HASH_SIZE * 2) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
else if (found_number) {
|
||||
return s;
|
||||
@@ -232,6 +276,15 @@ void uv_rwlock_init_checked(uv_rwlock_t* lock)
|
||||
}
|
||||
}
|
||||
|
||||
void uv_async_init_checked(uv_loop_t* loop, uv_async_t* async, uv_async_cb async_cb)
|
||||
{
|
||||
const int err = uv_async_init(loop, async, async_cb);
|
||||
if (err) {
|
||||
LOGERR(1, "uv_async_init failed, error " << uv_err_name(err));
|
||||
panic();
|
||||
}
|
||||
}
|
||||
|
||||
uv_loop_t* uv_default_loop_checked()
|
||||
{
|
||||
if (!is_main_thread()) {
|
||||
@@ -278,21 +331,22 @@ struct BackgroundJobTracker::Impl
|
||||
|
||||
void wait()
|
||||
{
|
||||
uint64_t last_msg_time = 0;
|
||||
do {
|
||||
bool is_empty = true;
|
||||
{
|
||||
MutexLock lock(m_lock);
|
||||
is_empty = m_jobs.empty();
|
||||
for (const auto& job : m_jobs) {
|
||||
LOGINFO(1, "waiting for " << job.second << " \"" << job.first << "\" jobs to finish");
|
||||
if (m_jobs.empty()) {
|
||||
return;
|
||||
}
|
||||
const uint64_t t = seconds_since_epoch();
|
||||
if (t != last_msg_time) {
|
||||
last_msg_time = t;
|
||||
for (const auto& job : m_jobs) {
|
||||
LOGINFO(1, "waiting for " << job.second << " \"" << job.first << "\" jobs to finish");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_empty) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
} while (1);
|
||||
}
|
||||
|
||||
@@ -314,8 +368,10 @@ struct BackgroundJobTracker::Impl
|
||||
LOGINFO(0, "background jobs running:" << log::const_buf(buf, s.m_pos));
|
||||
}
|
||||
|
||||
struct Compare { FORCEINLINE bool operator()(const char* a, const char* b) const { return strcmp(a, b) < 0; } };
|
||||
|
||||
uv_mutex_t m_lock;
|
||||
std::map<std::string, int32_t> m_jobs;
|
||||
std::map<const char*, int32_t, Compare> m_jobs;
|
||||
};
|
||||
|
||||
BackgroundJobTracker::BackgroundJobTracker() : m_impl(new Impl())
|
||||
@@ -327,12 +383,12 @@ BackgroundJobTracker::~BackgroundJobTracker()
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
void BackgroundJobTracker::start(const char* name)
|
||||
void BackgroundJobTracker::start_internal(const char* name)
|
||||
{
|
||||
m_impl->start(name);
|
||||
}
|
||||
|
||||
void BackgroundJobTracker::stop(const char* name)
|
||||
void BackgroundJobTracker::stop_internal(const char* name)
|
||||
{
|
||||
m_impl->stop(name);
|
||||
}
|
||||
|
||||
+12
-2
@@ -34,6 +34,9 @@
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
#define P2POOL_VERSION_MAJOR 2
|
||||
#define P2POOL_VERSION_MINOR 6
|
||||
|
||||
extern const char* VERSION;
|
||||
|
||||
template<typename T> struct not_implemented { enum { value = 0 }; };
|
||||
@@ -156,18 +159,25 @@ public:
|
||||
BackgroundJobTracker();
|
||||
~BackgroundJobTracker();
|
||||
|
||||
void start(const char* name);
|
||||
void stop(const char* name);
|
||||
template<size_t N> FORCEINLINE void start(const char (&name)[N]) { start_internal(name); }
|
||||
template<size_t N> FORCEINLINE void stop (const char (&name)[N]) { stop_internal (name); }
|
||||
|
||||
void wait();
|
||||
void print_status();
|
||||
|
||||
private:
|
||||
void start_internal(const char* name);
|
||||
void stop_internal(const char* name);
|
||||
|
||||
struct Impl;
|
||||
Impl* m_impl;
|
||||
};
|
||||
|
||||
extern BackgroundJobTracker bkg_jobs_tracker;
|
||||
|
||||
#define BACKGROUND_JOB_START(x) do { bkg_jobs_tracker.start(#x); } while (0)
|
||||
#define BACKGROUND_JOB_STOP(x) do { bkg_jobs_tracker.stop(#x); } while (0)
|
||||
|
||||
void set_main_thread();
|
||||
bool is_main_thread();
|
||||
|
||||
|
||||
+2
-1
@@ -60,6 +60,7 @@ typedef RWLock<true> WriteLock;
|
||||
void uv_cond_init_checked(uv_cond_t* cond);
|
||||
void uv_mutex_init_checked(uv_mutex_t* mutex);
|
||||
void uv_rwlock_init_checked(uv_rwlock_t* lock);
|
||||
void uv_async_init_checked(uv_loop_t* loop, uv_async_t* async, uv_async_cb async_cb);
|
||||
uv_loop_t* uv_default_loop_checked();
|
||||
|
||||
struct UV_LoopCallbackBase
|
||||
@@ -96,7 +97,7 @@ struct UV_LoopUserData
|
||||
, m_callbacks{}
|
||||
, m_callbacksToRun{}
|
||||
{
|
||||
uv_async_init(m_loop, m_async, async_cb);
|
||||
uv_async_init_checked(m_loop, m_async, async_cb);
|
||||
m_async->data = this;
|
||||
|
||||
uv_mutex_init_checked(&m_callbacksLock);
|
||||
|
||||
+51
-26
@@ -29,16 +29,14 @@ extern "C" {
|
||||
|
||||
namespace {
|
||||
|
||||
// public keys: 64 bytes -> 88 characters in base58
|
||||
// prefix (1 byte) + checksum (4 bytes) -> 7 characters in base58
|
||||
// 95 characters in total
|
||||
constexpr int ADDRESS_LENGTH = 95;
|
||||
|
||||
// Allow only regular addresses (no integrated addresses, no subaddresses)
|
||||
// Values taken from cryptonote_config.h (CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX)
|
||||
constexpr uint64_t valid_prefixes[] = { 18, 53, 24 };
|
||||
|
||||
constexpr std::array<int, 9> block_sizes{ 0, 2, 3, 5, 6, 7, 9, 10, 11 };
|
||||
constexpr int num_full_blocks = p2pool::Wallet::ADDRESS_LENGTH / block_sizes.back();
|
||||
constexpr int last_block_size = p2pool::Wallet::ADDRESS_LENGTH % block_sizes.back();
|
||||
|
||||
constexpr int block_sizes_lookup[11] = { 0, -1, 1, 2, -1, 3, 4, 5, -1, 6, 7 };
|
||||
|
||||
constexpr char alphabet[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||
@@ -112,8 +110,6 @@ bool Wallet::decode(const char* address)
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr int num_full_blocks = ADDRESS_LENGTH / block_sizes.back();
|
||||
constexpr int last_block_size = ADDRESS_LENGTH % block_sizes.back();
|
||||
constexpr int last_block_size_index = block_sizes_lookup[last_block_size];
|
||||
|
||||
static_assert(last_block_size_index >= 0, "Check ADDRESS_LENGTH");
|
||||
@@ -150,11 +146,13 @@ bool Wallet::decode(const char* address)
|
||||
|
||||
m_prefix = data[0];
|
||||
|
||||
if (m_prefix == valid_prefixes[0]) m_type = NetworkType::Mainnet;
|
||||
if (m_prefix == valid_prefixes[1]) m_type = NetworkType::Testnet;
|
||||
if (m_prefix == valid_prefixes[2]) m_type = NetworkType::Stagenet;
|
||||
switch (m_prefix)
|
||||
{
|
||||
case valid_prefixes[0]: m_type = NetworkType::Mainnet; break;
|
||||
case valid_prefixes[1]: m_type = NetworkType::Testnet; break;
|
||||
case valid_prefixes[2]: m_type = NetworkType::Stagenet; break;
|
||||
|
||||
if (m_type == NetworkType::Invalid) {
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -184,35 +182,62 @@ bool Wallet::assign(const hash& spend_pub_key, const hash& view_pub_key, Network
|
||||
return false;
|
||||
}
|
||||
|
||||
m_prefix = 0;
|
||||
switch (type)
|
||||
{
|
||||
case NetworkType::Mainnet: m_prefix = valid_prefixes[0]; break;
|
||||
case NetworkType::Testnet: m_prefix = valid_prefixes[1]; break;
|
||||
case NetworkType::Stagenet: m_prefix = valid_prefixes[2]; break;
|
||||
default: m_prefix = 0; break;
|
||||
}
|
||||
|
||||
m_spendPublicKey = spend_pub_key;
|
||||
m_viewPublicKey = view_pub_key;
|
||||
m_checksum = 0;
|
||||
|
||||
uint8_t data[1 + HASH_SIZE * 2];
|
||||
data[0] = static_cast<uint8_t>(m_prefix);
|
||||
memcpy(data + 1, spend_pub_key.h, HASH_SIZE);
|
||||
memcpy(data + 1 + HASH_SIZE, view_pub_key.h, HASH_SIZE);
|
||||
|
||||
uint8_t md[200];
|
||||
keccak(data, sizeof(data), md);
|
||||
|
||||
memcpy(&m_checksum, md, sizeof(m_checksum));
|
||||
|
||||
m_type = type;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wallet::get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag) const
|
||||
void Wallet::encode(char (&buf)[ADDRESS_LENGTH]) const
|
||||
{
|
||||
uint8_t data[1 + HASH_SIZE * 2 + sizeof(m_checksum)];
|
||||
|
||||
data[0] = static_cast<uint8_t>(m_prefix);
|
||||
memcpy(data + 1, m_spendPublicKey.h, HASH_SIZE);
|
||||
memcpy(data + 1 + HASH_SIZE, m_viewPublicKey.h, HASH_SIZE);
|
||||
memcpy(data + 1 + HASH_SIZE * 2, &m_checksum, sizeof(m_checksum));
|
||||
|
||||
for (int i = 0; i <= num_full_blocks; ++i) {
|
||||
uint64_t n = 0;
|
||||
for (int j = 0; (j < 8) && (i * sizeof(uint64_t) + j < sizeof(data)); ++j) {
|
||||
n = (n << 8) | data[i * sizeof(uint64_t) + j];
|
||||
}
|
||||
for (int j = ((i < num_full_blocks) ? block_sizes.back() : last_block_size) - 1; j >= 0; --j) {
|
||||
const int digit = n % alphabet_size;
|
||||
n /= alphabet_size;
|
||||
buf[i * block_sizes.back() + j] = alphabet[digit];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Wallet::get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag, const uint8_t* expected_view_tag) const
|
||||
{
|
||||
hash derivation;
|
||||
if (!generate_key_derivation(m_viewPublicKey, txkey_sec, output_index, derivation, view_tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!derive_public_key(derivation, output_index, m_spendPublicKey, eph_public_key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Wallet::get_eph_public_key_with_view_tag(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t expected_view_tag) const
|
||||
{
|
||||
hash derivation;
|
||||
uint8_t view_tag;
|
||||
if (!generate_key_derivation(m_viewPublicKey, txkey_sec, output_index, derivation, view_tag) || (view_tag != expected_view_tag)) {
|
||||
if (expected_view_tag && (view_tag != *expected_view_tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+22
-7
@@ -24,25 +24,40 @@ namespace p2pool {
|
||||
class Wallet
|
||||
{
|
||||
public:
|
||||
// public keys: 64 bytes -> 88 characters in base58
|
||||
// prefix (1 byte) + checksum (4 bytes) -> 7 characters in base58
|
||||
// 95 characters in total
|
||||
static constexpr int ADDRESS_LENGTH = 95;
|
||||
|
||||
explicit Wallet(const char* address);
|
||||
|
||||
Wallet(const Wallet& w);
|
||||
Wallet& operator=(const Wallet& w);
|
||||
|
||||
FORCEINLINE bool valid() const { return m_type != NetworkType::Invalid; }
|
||||
FORCEINLINE NetworkType type() const { return m_type; }
|
||||
|
||||
bool decode(const char* address);
|
||||
bool assign(const hash& spend_pub_key, const hash& view_pub_key, NetworkType type);
|
||||
|
||||
void encode(char (&buf)[ADDRESS_LENGTH]) const;
|
||||
|
||||
FORCEINLINE std::string encode() const
|
||||
{
|
||||
char buf[ADDRESS_LENGTH];
|
||||
encode(buf);
|
||||
return std::string(buf, buf + ADDRESS_LENGTH);
|
||||
}
|
||||
|
||||
bool get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag, const uint8_t* expected_view_tag = nullptr) const;
|
||||
|
||||
FORCEINLINE bool operator<(const Wallet& w) const { return (m_spendPublicKey < w.m_spendPublicKey) || ((m_spendPublicKey == w.m_spendPublicKey) && (m_viewPublicKey < w.m_viewPublicKey)); }
|
||||
FORCEINLINE bool operator==(const Wallet& w) const { return (m_spendPublicKey == w.m_spendPublicKey) && (m_viewPublicKey == w.m_viewPublicKey); }
|
||||
|
||||
FORCEINLINE uint64_t prefix() const { return m_prefix; }
|
||||
FORCEINLINE const hash& spend_public_key() const { return m_spendPublicKey; }
|
||||
FORCEINLINE const hash& view_public_key() const { return m_viewPublicKey; }
|
||||
|
||||
bool get_eph_public_key(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t& view_tag) const;
|
||||
bool get_eph_public_key_with_view_tag(const hash& txkey_sec, size_t output_index, hash& eph_public_key, uint8_t expected_view_tag) const;
|
||||
|
||||
FORCEINLINE bool operator<(const Wallet& w) const { return m_spendPublicKey < w.m_spendPublicKey; }
|
||||
FORCEINLINE bool operator==(const Wallet& w) const { return m_spendPublicKey == w.m_spendPublicKey; }
|
||||
FORCEINLINE uint32_t checksum() const { return m_checksum; }
|
||||
FORCEINLINE NetworkType type() const { return m_type; }
|
||||
|
||||
private:
|
||||
uint64_t m_prefix;
|
||||
|
||||
+21
-3
@@ -22,6 +22,7 @@ set(HEADERS
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
src/block_template_tests.cpp
|
||||
src/crypto_tests.cpp
|
||||
src/difficulty_type_tests.cpp
|
||||
src/hash_tests.cpp
|
||||
@@ -55,13 +56,30 @@ set(SOURCES
|
||||
../src/zmq_reader.cpp
|
||||
)
|
||||
|
||||
if (NOT STATIC_BINARY AND NOT STATIC_LIBS)
|
||||
include(FindCURL)
|
||||
endif()
|
||||
|
||||
if (CURL_INCLUDE_DIRS)
|
||||
include_directories(${CURL_INCLUDE_DIRS})
|
||||
else()
|
||||
include_directories(../external/src/curl/include)
|
||||
endif()
|
||||
|
||||
if ((CMAKE_CXX_COMPILER_ID MATCHES MSVC) OR STATIC_BINARY OR STATIC_LIBS)
|
||||
set(UV_INCLUDE_DIR ../external/src/libuv/include)
|
||||
set(ZMQ_INCLUDE_DIR ../external/src/libzmq/include)
|
||||
else()
|
||||
find_path(UV_INCLUDE_DIR NAMES uv.h PATH_SUFFIXES "include")
|
||||
find_path(ZMQ_INCLUDE_DIR NAMES zmq.h PATH_SUFFIXES "include")
|
||||
endif()
|
||||
|
||||
include_directories(../src)
|
||||
include_directories(../external/src)
|
||||
include_directories(../external/src/cryptonote)
|
||||
include_directories(../external/src/curl/include)
|
||||
include_directories(../external/src/libuv/include)
|
||||
include_directories(${UV_INCLUDE_DIR})
|
||||
include_directories(../external/src/cppzmq)
|
||||
include_directories(../external/src/libzmq/include)
|
||||
include_directories(${ZMQ_INCLUDE_DIR})
|
||||
include_directories(../external/src/RandomX/src)
|
||||
include_directories(../external/src/rapidjson/include)
|
||||
include_directories(../external/src/robin-hood-hashing/src/include)
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* This file is part of the Monero P2Pool <https://github.com/SChernykh/p2pool>
|
||||
* Copyright (c) 2021 SChernykh <https://github.com/SChernykh>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "crypto.h"
|
||||
#include "block_template.h"
|
||||
#include "mempool.h"
|
||||
#include "side_chain.h"
|
||||
#include "wallet.h"
|
||||
#include "keccak.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
TEST(block_template, update)
|
||||
{
|
||||
init_crypto_cache();
|
||||
|
||||
SideChain sidechain(nullptr, NetworkType::Mainnet);
|
||||
BlockTemplate tpl(&sidechain, nullptr);
|
||||
|
||||
auto H = [](const char* s)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << s;
|
||||
hash result;
|
||||
ss >> result;
|
||||
return result;
|
||||
};
|
||||
|
||||
MinerData data;
|
||||
data.major_version = 16;
|
||||
data.height = 2762973;
|
||||
data.prev_id = H("81a0260b29d5224e88d04b11faff321fbdc11c4570779386b2a1817a86dc622c");
|
||||
data.seed_hash = H("33d0fb381466f04d6a1919ced3b698f54a28add3da5a6479b096c67df7a4974c");
|
||||
data.difficulty = { 300346053753ULL, 0 };
|
||||
data.median_weight = 300000;
|
||||
data.already_generated_coins = 18204981557254756780ULL;
|
||||
data.median_timestamp = (1ULL << 35) - 2;
|
||||
|
||||
Mempool mempool;
|
||||
Wallet wallet("44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg");
|
||||
|
||||
// Test 1: empty template
|
||||
tpl.update(data, mempool, &wallet);
|
||||
|
||||
const PoolBlock* b = tpl.pool_block_template();
|
||||
ASSERT_EQ(b->m_sidechainId, H("b708e3e456d97c43a7fcbd7b4e7aa29bdf45cd909bba07f915cb5f1d805433e6"));
|
||||
|
||||
std::vector<uint8_t> blobs;
|
||||
uint64_t height;
|
||||
difficulty_type diff, sidechain_diff;
|
||||
hash seed_hash;
|
||||
size_t nonce_offset;
|
||||
uint32_t template_id;
|
||||
tpl.get_hashing_blobs(0, 10000, blobs, height, diff, sidechain_diff, seed_hash, nonce_offset, template_id);
|
||||
|
||||
ASSERT_EQ(height, 2762973);
|
||||
ASSERT_EQ(diff, 300346053753ULL);
|
||||
ASSERT_EQ(sidechain_diff, sidechain.difficulty());
|
||||
ASSERT_EQ(seed_hash, data.seed_hash);
|
||||
ASSERT_EQ(nonce_offset, 39);
|
||||
ASSERT_EQ(template_id, 1);
|
||||
|
||||
hash blobs_hash;
|
||||
keccak(blobs.data(), static_cast<int>(blobs.size()), blobs_hash.h, HASH_SIZE);
|
||||
ASSERT_EQ(blobs_hash, H("e9154971a27c412175562d23ab458b0d3cf780a8bcecf62ff3f667fed9d3bc1d"));
|
||||
|
||||
// Test 2: mempool with high fee and low fee transactions, it must choose high fee transactions
|
||||
for (uint64_t i = 0; i < 512; ++i) {
|
||||
TxMempoolData tx;
|
||||
*reinterpret_cast<uint64_t*>(tx.id.h) = i;
|
||||
tx.fee = (i < 256) ? 30000000 : 60000000;
|
||||
tx.weight = 1500;
|
||||
mempool.add(tx);
|
||||
}
|
||||
|
||||
tpl.update(data, mempool, &wallet);
|
||||
|
||||
for (size_t i = 1; i < b->m_transactions.size(); ++i) {
|
||||
ASSERT_GE(*reinterpret_cast<const uint64_t*>(b->m_transactions[i].h), 256);
|
||||
}
|
||||
|
||||
destroy_crypto_cache();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
static const difficulty_type max_diff{ std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max() };
|
||||
|
||||
TEST(difficulty_type, constructors)
|
||||
{
|
||||
difficulty_type diff;
|
||||
@@ -56,10 +58,7 @@ TEST(difficulty_type, target)
|
||||
}
|
||||
|
||||
// diff = max
|
||||
{
|
||||
difficulty_type d(std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max());
|
||||
ASSERT_EQ(d.target(), 1);
|
||||
}
|
||||
ASSERT_EQ(max_diff.target(), 1);
|
||||
|
||||
// diff = 2^32
|
||||
{
|
||||
@@ -74,18 +73,35 @@ TEST(difficulty_type, target)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(difficulty_type, sum)
|
||||
TEST(difficulty_type, add_sub)
|
||||
{
|
||||
auto check = [](const difficulty_type& a, const difficulty_type& b, const difficulty_type& sum)
|
||||
{
|
||||
difficulty_type result1 = a + b;
|
||||
difficulty_type result2 = a;
|
||||
result2 += b;
|
||||
|
||||
ASSERT_EQ(result1, sum);
|
||||
ASSERT_EQ(result2, sum);
|
||||
|
||||
ASSERT_EQ(sum - a, b);
|
||||
ASSERT_EQ(sum - b, a);
|
||||
|
||||
result1 -= a;
|
||||
ASSERT_EQ(result1, b);
|
||||
|
||||
result2 -= b;
|
||||
ASSERT_EQ(result2, a);
|
||||
};
|
||||
|
||||
// No carry
|
||||
{
|
||||
difficulty_type diff[4] = { { 0, 0 }, { 1, 0 }, { 0, 1 }, { 1, 1 } };
|
||||
|
||||
for (int i = 0; i <= 3; ++i) {
|
||||
for (int j = 0; j <= 3; ++j) {
|
||||
difficulty_type a = diff[i];
|
||||
a += diff[j];
|
||||
ASSERT_EQ(a.lo, diff[i].lo + diff[j].lo);
|
||||
ASSERT_EQ(a.hi, diff[i].hi + diff[j].hi);
|
||||
difficulty_type sum(diff[i].lo + diff[j].lo, diff[i].hi + diff[j].hi);
|
||||
check(diff[i], diff[j], sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,18 +110,307 @@ TEST(difficulty_type, sum)
|
||||
{
|
||||
difficulty_type a(11400714819323198485ull, 0);
|
||||
difficulty_type b(15975348984942515101ull, 0);
|
||||
a += b;
|
||||
ASSERT_EQ(a.lo, 8929319730556161970ull);
|
||||
ASSERT_EQ(a.hi, 1);
|
||||
difficulty_type sum(8929319730556161970ull, 1);
|
||||
check(a, b, sum);
|
||||
}
|
||||
|
||||
// Carry (edge case)
|
||||
{
|
||||
difficulty_type a(std::numeric_limits<uint64_t>::max(), 0);
|
||||
difficulty_type b(1, 0);
|
||||
a += b;
|
||||
difficulty_type sum(0, 1);
|
||||
check(a, b, sum);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(difficulty_type, mul_div)
|
||||
{
|
||||
auto check = [](const difficulty_type& a, uint64_t b, const difficulty_type& product)
|
||||
{
|
||||
ASSERT_EQ(a * b, product);
|
||||
|
||||
difficulty_type result = a;
|
||||
result *= b;
|
||||
ASSERT_EQ(result, product);
|
||||
|
||||
if (b) {
|
||||
ASSERT_EQ(result / b, a);
|
||||
|
||||
difficulty_type tmp = result;
|
||||
tmp /= difficulty_type(b, 0);
|
||||
ASSERT_EQ(tmp, a);
|
||||
|
||||
difficulty_type tmp2 = result;
|
||||
tmp2 /= b;
|
||||
ASSERT_EQ(tmp2, a);
|
||||
}
|
||||
};
|
||||
|
||||
// (2^128 - 1) * 0 = 0
|
||||
check(max_diff, 0, { 0, 0 });
|
||||
|
||||
// (2^128 - 1) * 1 = 2^128 - 1
|
||||
check(max_diff, 1, max_diff);
|
||||
|
||||
// 5057672949897463733145855 * 67280421310721 = 2^128 - 1
|
||||
check({ 18446744073709277439ull, 274176ull }, 67280421310721ull, max_diff);
|
||||
|
||||
// 10^19 * 10 = 10^20
|
||||
check({ 10000000000000000000ull, 0 }, 10, { 7766279631452241920ull, 5 });
|
||||
|
||||
// 10^20 * 10 = 10^21
|
||||
check({ 7766279631452241920ull, 5 }, 10, { 3875820019684212736ull, 54 });
|
||||
|
||||
// 0 * (2^64 - 1) = 0
|
||||
check({ 0, 0 }, std::numeric_limits<uint64_t>::max(), { 0, 0 });
|
||||
|
||||
// 1 * (2^64 - 1) = 2^64 - 1
|
||||
check({ 1, 0 }, std::numeric_limits<uint64_t>::max(), { std::numeric_limits<uint64_t>::max(), 0 });
|
||||
|
||||
// 2^64 * (2^64 - 1) = 2^128 - 2^64
|
||||
check({ 0, 1 }, std::numeric_limits<uint64_t>::max(), { 0, std::numeric_limits<uint64_t>::max() });
|
||||
|
||||
// (2^64 + 1) * (2^64 - 1) = 2^128 - 1
|
||||
check({ 1, 1 }, std::numeric_limits<uint64_t>::max(), max_diff);
|
||||
|
||||
// 2753074036095 * 6700417 = 2^64 - 1
|
||||
check({ 2753074036095ull, 0 }, 6700417, { std::numeric_limits<uint64_t>::max(), 0 });
|
||||
|
||||
// 2^32 * 2^32 = 2^64
|
||||
check({ 4294967296ull, 0 }, 4294967296ull, { 0, 1 });
|
||||
|
||||
// 274177 * 67280421310721 = 2^64 + 1
|
||||
check({ 274177, 0 }, 67280421310721ull, { 1, 1 });
|
||||
|
||||
// Powers of 2
|
||||
{
|
||||
difficulty_type a(1, 0);
|
||||
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
ASSERT_EQ(a.lo, 1ull << i);
|
||||
ASSERT_EQ(a.hi, 0);
|
||||
a *= 2;
|
||||
|
||||
difficulty_type b = a;
|
||||
b /= 2;
|
||||
ASSERT_EQ(b.lo, 1ull << i);
|
||||
ASSERT_EQ(b.hi, 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
ASSERT_EQ(a.lo, 0);
|
||||
ASSERT_EQ(a.hi, 1ull << i);
|
||||
a *= 2;
|
||||
|
||||
if (i < 63) {
|
||||
difficulty_type b = a;
|
||||
b /= 2;
|
||||
ASSERT_EQ(b.lo, 0);
|
||||
ASSERT_EQ(b.hi, 1ull << i);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(a.lo, 0);
|
||||
ASSERT_EQ(a.hi, 1);
|
||||
ASSERT_EQ(a.hi, 0);
|
||||
}
|
||||
|
||||
// No carry
|
||||
check({ 123, 456 }, 789, { 97047, 359784 });
|
||||
}
|
||||
|
||||
static NOINLINE difficulty_type div128_ref(difficulty_type a, difficulty_type b)
|
||||
{
|
||||
difficulty_type result{};
|
||||
|
||||
while (a >= b) {
|
||||
difficulty_type t = b;
|
||||
difficulty_type q{ 1, 0 };
|
||||
while (a - t >= t) {
|
||||
t += t;
|
||||
q += q;
|
||||
}
|
||||
a -= t;
|
||||
result += q;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST(difficulty_type, div128)
|
||||
{
|
||||
auto check = [](difficulty_type a, difficulty_type b, difficulty_type result)
|
||||
{
|
||||
ASSERT_EQ(div128_ref(a, b), result);
|
||||
ASSERT_EQ(a / b, result);
|
||||
a /= b;
|
||||
ASSERT_EQ(a, result);
|
||||
};
|
||||
|
||||
// (2^128 - 1) / (2^128 - 1) = 1
|
||||
check(max_diff, max_diff, { 1, 0 });
|
||||
|
||||
// (2^128 - 1) / (2^64 - 1) = 2^64 + 1
|
||||
check(max_diff, { std::numeric_limits<uint64_t>::max(), 0 }, { 1, 1 });
|
||||
|
||||
// (2^128 - 1) / 2^64 = 2^64 - 1
|
||||
check(max_diff, { 0, 1 }, { std::numeric_limits<uint64_t>::max(), 0 });
|
||||
|
||||
// (2^128 - 1) / (2^64 + 1) = 2^64 - 1
|
||||
check(max_diff, { 1, 1 }, { std::numeric_limits<uint64_t>::max(), 0 });
|
||||
|
||||
// (2^128 - 1) / 8100430714362380904069067128193 = 42007935
|
||||
check(max_diff, { 439125228929, 439125228929 }, { 42007935, 0 });
|
||||
|
||||
// (2^128 - 2^64) / (2^64 + 1) = 2^64 - 2
|
||||
check({ 0, std::numeric_limits<uint64_t>::max() }, { 1, 1 }, { std::numeric_limits<uint64_t>::max() - 1, 0 });
|
||||
|
||||
// (2^128 - 2^64) / 2^64 = 2^64 - 1
|
||||
check({ 0, std::numeric_limits<uint64_t>::max() }, { 0, 1 }, { std::numeric_limits<uint64_t>::max(), 0 });
|
||||
|
||||
// (2^128 - 2^64) / (2^64 - 1) = 2^64
|
||||
check({ 0, std::numeric_limits<uint64_t>::max() }, { std::numeric_limits<uint64_t>::max(), 0 }, { 0, 1 });
|
||||
|
||||
{
|
||||
difficulty_type a = max_diff - 4;
|
||||
|
||||
// (2^128 - 5) / 2002733033099709041094789607565039 = 169909
|
||||
check(a, { 7565587230673184495, 108568375269759 }, { 169909, 0 });
|
||||
|
||||
// (2^128 - 5) / 2002733033099709041094789607565040 = 169908
|
||||
check(a, { 7565587230673184496, 108568375269759 }, { 169908, 0 });
|
||||
|
||||
a -= 1;
|
||||
|
||||
// (2^128 - 6) / 2002733033099709041094789607565039 = 169908
|
||||
check(a, { 7565587230673184495, 108568375269759 }, { 169908, 0 });
|
||||
|
||||
// (2^128 - 6) / 2002733033099709041094789607565038 = 169909
|
||||
check(a, { 7565587230673184494, 108568375269759 }, { 169909, 0 });
|
||||
}
|
||||
|
||||
// Powers of 2
|
||||
for (difficulty_type i{ 1, 0 }, j = max_diff; !i.empty(); i += i, j /= 2) {
|
||||
check(max_diff, i, j);
|
||||
}
|
||||
|
||||
// Trivial tests
|
||||
check({ 0, 3 }, { 0, 1 }, { 3, 0 });
|
||||
check({ 0, 3 }, { 1, 1 }, { 2, 0 });
|
||||
check({ 123 * 4 - 1, 456 * 4 }, { 123, 456 }, { 3, 0 });
|
||||
check({ 123 * 4, 456 * 4 }, { 123, 456 }, { 4, 0 });
|
||||
|
||||
// Exhaustive tests (top 8 bits of each number)
|
||||
for (uint64_t i = 1; i < 256; ++i) {
|
||||
for (uint64_t j = 1; j < 256; ++j) {
|
||||
const difficulty_type a{ 0, i << 56 };
|
||||
const difficulty_type b{ 0, j << 56 };
|
||||
{
|
||||
difficulty_type t = a;
|
||||
t /= b;
|
||||
ASSERT_EQ(t.lo, i / j);
|
||||
ASSERT_EQ(t.hi, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bit patterns
|
||||
std::vector<difficulty_type> patterns;
|
||||
|
||||
// 2^N-1, 2^N, 2^N+1
|
||||
for (uint64_t i = 0; i < 128; ++i) {
|
||||
difficulty_type t;
|
||||
reinterpret_cast<uint64_t*>(&t)[i / 64] |= 1ull << (i % 64);
|
||||
patterns.emplace_back(t - 1);
|
||||
patterns.emplace_back(t);
|
||||
patterns.emplace_back(t + 1);
|
||||
}
|
||||
|
||||
// 2^N+2^M, 2^N-2^M
|
||||
bool check_bits[128] = {};
|
||||
for (uint64_t i = 64 - 4; i < 64 + 4; ++i) {
|
||||
check_bits[i] = true;
|
||||
}
|
||||
for (uint64_t i = 128 - 8; i < 128; ++i) {
|
||||
check_bits[i] = true;
|
||||
}
|
||||
for (uint64_t i = 0; i < 128; ++i) {
|
||||
if (!check_bits[i]) {
|
||||
continue;
|
||||
}
|
||||
difficulty_type t1;
|
||||
reinterpret_cast<uint64_t*>(&t1)[i / 64] = 1ull << (i % 64);
|
||||
for (uint64_t j = i + 1; j < 128; ++j) {
|
||||
if (!check_bits[j]) {
|
||||
continue;
|
||||
}
|
||||
difficulty_type t2;
|
||||
reinterpret_cast<uint64_t*>(&t2)[j / 64] = 1ull << (j % 64);
|
||||
patterns.emplace_back(t2 + t1);
|
||||
patterns.emplace_back(t2 - t1);
|
||||
}
|
||||
}
|
||||
|
||||
// All previous patterns, but ~X
|
||||
for (size_t i = 0, n = patterns.size(); i < n; ++i) {
|
||||
patterns.emplace_back(~patterns[i].lo, ~patterns[i].hi);
|
||||
}
|
||||
|
||||
std::sort(patterns.begin(), patterns.end());
|
||||
patterns.erase(std::unique(patterns.begin(), patterns.end()), patterns.end());
|
||||
|
||||
// remove 0
|
||||
patterns.erase(patterns.begin());
|
||||
|
||||
for (size_t i = 0, n = patterns.size(); i < n; ++i) {
|
||||
const difficulty_type& a = patterns[i];
|
||||
for (size_t j = i + 1; j < n; ++j) {
|
||||
const difficulty_type& b = patterns[j];
|
||||
ASSERT_EQ(div128_ref(b, a), b / a);
|
||||
}
|
||||
}
|
||||
|
||||
// Random tests with fixed seed
|
||||
std::mt19937_64 r(0);
|
||||
|
||||
for (uint64_t i = 0; i < 10000000; ++i) {
|
||||
// Random number of bits [1, 63]
|
||||
const uint64_t N = (r() % 63) + 1;
|
||||
|
||||
// Random multiplier [1, 2^N - 1]
|
||||
uint64_t k;
|
||||
do {
|
||||
k = r() & ((1ull << N) - 1);
|
||||
} while (k == 0);
|
||||
|
||||
uint64_t t;
|
||||
const uint64_t max_a = udiv128(1, 0, k + 1, &t);
|
||||
|
||||
// Random number [2^64, 2^128 / (k + 1)]
|
||||
difficulty_type a{ r(), 0 };
|
||||
do {
|
||||
a.hi = r() % max_a;
|
||||
} while (a.hi == 0);
|
||||
|
||||
difficulty_type b1 = a * k;
|
||||
difficulty_type b2 = b1 - 1;
|
||||
difficulty_type b3 = b1 + a;
|
||||
difficulty_type b4 = b3 - 1;
|
||||
|
||||
b1 /= a;
|
||||
ASSERT_EQ(b1.lo, k);
|
||||
ASSERT_EQ(b1.hi, 0);
|
||||
|
||||
b2 /= a;
|
||||
ASSERT_EQ(b2.lo, k - 1);
|
||||
ASSERT_EQ(b2.hi, 0);
|
||||
|
||||
b3 /= a;
|
||||
ASSERT_EQ(b3.lo, k + 1);
|
||||
ASSERT_EQ(b3.hi, 0);
|
||||
|
||||
b4 /= a;
|
||||
ASSERT_EQ(b4.lo, k);
|
||||
ASSERT_EQ(b4.hi, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ TEST(pool_block, deserialize)
|
||||
f.read(reinterpret_cast<char*>(buf.data()), buf.size());
|
||||
ASSERT_EQ(f.good(), true);
|
||||
|
||||
ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain, nullptr), 0);
|
||||
ASSERT_EQ(b.deserialize(buf.data(), buf.size(), sidechain, nullptr, false), 0);
|
||||
|
||||
size_t header_size, miner_tx_size;
|
||||
int outputs_offset, outputs_blob_size;
|
||||
@@ -137,7 +137,7 @@ TEST(pool_block, verify)
|
||||
p += sizeof(uint32_t);
|
||||
|
||||
ASSERT_TRUE(p + n <= e);
|
||||
ASSERT_EQ(b.deserialize(p, n, sidechain, nullptr), 0);
|
||||
ASSERT_EQ(b.deserialize(p, n, sidechain, nullptr, false), 0);
|
||||
p += n;
|
||||
|
||||
sidechain.add_block(b);
|
||||
|
||||
+40
-11
@@ -21,7 +21,7 @@
|
||||
|
||||
namespace p2pool {
|
||||
|
||||
TEST(wallet, decode)
|
||||
TEST(wallet, input_output)
|
||||
{
|
||||
// No data
|
||||
{
|
||||
@@ -59,10 +59,13 @@ TEST(wallet, decode)
|
||||
ASSERT_EQ(w.valid(), false);
|
||||
}
|
||||
|
||||
auto check = [](NetworkType t, const char* address, const char* spendkey, const char* viewkey)
|
||||
auto check = [](NetworkType t, uint64_t prefix, uint32_t checksum, const char* address, const char* spendkey, const char* viewkey)
|
||||
{
|
||||
// Test Wallet::decode()
|
||||
Wallet w(address);
|
||||
ASSERT_EQ(w.type(), t);
|
||||
ASSERT_EQ(w.prefix(), prefix);
|
||||
ASSERT_EQ(w.checksum(), checksum);
|
||||
|
||||
char buf[log::Stream::BUF_SIZE + 1];
|
||||
log::Stream s(buf);
|
||||
@@ -73,53 +76,79 @@ TEST(wallet, decode)
|
||||
s.m_pos = 0;
|
||||
s << w.view_public_key();
|
||||
ASSERT_EQ(memcmp(buf, viewkey, HASH_SIZE * 2), 0);
|
||||
|
||||
// Test Wallet::assign()
|
||||
Wallet w2(nullptr);
|
||||
w2.assign(w.spend_public_key(), w.view_public_key(), w.type());
|
||||
|
||||
ASSERT_EQ(w2.prefix(), w.prefix());
|
||||
ASSERT_EQ(w2.spend_public_key(), w.spend_public_key());
|
||||
ASSERT_EQ(w2.view_public_key(), w.view_public_key());
|
||||
ASSERT_EQ(w2.checksum(), w.checksum());
|
||||
ASSERT_EQ(w2.type(), w.type());
|
||||
|
||||
// Test Wallet::encode()
|
||||
const std::string s0 = address;
|
||||
const std::string s1 = w.encode();
|
||||
const std::string s2 = w2.encode();
|
||||
ASSERT_EQ(s1, s0);
|
||||
ASSERT_EQ(s2, s0);
|
||||
|
||||
// Test Wallet::encode(buf)
|
||||
char buf1[Wallet::ADDRESS_LENGTH] = {};
|
||||
w.encode(buf1);
|
||||
ASSERT_EQ(memcmp(buf1, address, Wallet::ADDRESS_LENGTH), 0);
|
||||
|
||||
char buf2[Wallet::ADDRESS_LENGTH] = {};
|
||||
w2.encode(buf2);
|
||||
ASSERT_EQ(memcmp(buf2, address, Wallet::ADDRESS_LENGTH), 0);
|
||||
};
|
||||
|
||||
// Correct mainnet addresses
|
||||
check(
|
||||
NetworkType::Mainnet, "49ccoSmrBTPJd5yf8VYCULh4J5rHQaXP1TeC8Cnqhd5H9Y2cMwkJ9w42euLmMghKtCiQcgZEiGYW1K6Ae4biZ7w1HLSexS6",
|
||||
NetworkType::Mainnet, 18, 0xA345C1C9UL, "49ccoSmrBTPJd5yf8VYCULh4J5rHQaXP1TeC8Cnqhd5H9Y2cMwkJ9w42euLmMghKtCiQcgZEiGYW1K6Ae4biZ7w1HLSexS6",
|
||||
"d2e232e441546a695b27187692d035ef7be5c54692700c9f470dcd706753a833", "06f68970da46f709e2b4d0ffabd0d1f78ea6717786b5766c25c259111f212490"
|
||||
);
|
||||
|
||||
check(
|
||||
NetworkType::Mainnet, "45JHuqGBSqUXUyZx95H4C2J5aEL4zFjM3jpTmMTESPXPa3jmtSQWYezHX7r4A2xPQNBGsQupJqmPhRZb2QgBcEWRDQ9ywwR",
|
||||
NetworkType::Mainnet, 18, 0x8C8FB6E6UL, "45JHuqGBSqUXUyZx95H4C2J5aEL4zFjM3jpTmMTESPXPa3jmtSQWYezHX7r4A2xPQNBGsQupJqmPhRZb2QgBcEWRDQ9ywwR",
|
||||
"60fe176eaf3cffb63df130bc25036b661b947900941052fffe6ff4b51fc4f2c5", "9387910b0a2e4f62c32621b77ddbeb3d6c0054e5ed9bc492d87bab1a1eef366d"
|
||||
);
|
||||
|
||||
check(
|
||||
NetworkType::Mainnet, "43S5vhReDY4fJs99DBZtFS8JoJVNG17iaAVAARvRT8xzSYZqnJfXfTACLrZUzoBHQKhiJZCWCpqB4Kf3c64CEagdSRXd5D7",
|
||||
NetworkType::Mainnet, 18, 0x0E705A56UL, "43S5vhReDY4fJs99DBZtFS8JoJVNG17iaAVAARvRT8xzSYZqnJfXfTACLrZUzoBHQKhiJZCWCpqB4Kf3c64CEagdSRXd5D7",
|
||||
"2fc2f902659541e50753853ddb96912baf55f26bebe7d338b5c2239c437ddb98", "b814951166253543cfb0e1b8bdea58f366de824fddb8ef6f895fcf631873f6e1"
|
||||
);
|
||||
|
||||
// Correct testnet addresses
|
||||
check(
|
||||
NetworkType::Testnet, "9x6aEN1yd2WhPMPw89LV5LLK1ZFe6N8xiAm18Ay4q1U4LKMde7MpDdPRN6GiiGCJMVTHuptGGmfj2Qfp2vcKSRSG79HJrQn",
|
||||
NetworkType::Testnet, 53, 0x6F896672UL, "9x6aEN1yd2WhPMPw89LV5LLK1ZFe6N8xiAm18Ay4q1U4LKMde7MpDdPRN6GiiGCJMVTHuptGGmfj2Qfp2vcKSRSG79HJrQn",
|
||||
"821623ac165f07f172c86980254a43737332fd89ca36d33a57dc02d8026d9173", "7c55413e672f8691a9211eac6003109d2fdf224ba72c4d8d82353427a02bc136"
|
||||
);
|
||||
|
||||
check(
|
||||
NetworkType::Testnet, "9zsJP6KFF6ZGern5UkR7gyRXHFRTba6jG8JKnfzDySeqEdwPZaD8MNYGkjyADdVpWs7rXgyeu712JdxhX2k7d9SNB4TdRdS",
|
||||
NetworkType::Testnet, 53, 0x4124092AUL, "9zsJP6KFF6ZGern5UkR7gyRXHFRTba6jG8JKnfzDySeqEdwPZaD8MNYGkjyADdVpWs7rXgyeu712JdxhX2k7d9SNB4TdRdS",
|
||||
"cb366a3b44f6aa5d94e03db06325b6929b9e75dbf19dcf2ba2d14eb2efa53651", "8789afa33dca295e301baef826cec028fa22b831822c1bdcf8a847a43a3bff59"
|
||||
);
|
||||
|
||||
check(
|
||||
NetworkType::Testnet, "A1SqL5oPjh8Km1At7mao7U1fNjWkzeSwvQ39GimMqvhBF3FUoJhx1zxL2i6XbHzzAXDhKetiwSmYQeVwG6sUgwJuEqPyjWq",
|
||||
NetworkType::Testnet, 53, 0x0AC6459FUL, "A1SqL5oPjh8Km1At7mao7U1fNjWkzeSwvQ39GimMqvhBF3FUoJhx1zxL2i6XbHzzAXDhKetiwSmYQeVwG6sUgwJuEqPyjWq",
|
||||
"da78298fb6eb8f702698bec873bad703f4a51e1377a66d89ba977ca7f43b8e53", "eeb348f70afad971c50aa062f1d1544be64ef9cdc12475e030f2d295305e6e7a"
|
||||
);
|
||||
|
||||
// Correct stagenet addresses
|
||||
check(
|
||||
NetworkType::Stagenet, "55AJ4jJBhV6JsoqrEsAazTLrJjg9SA1SFReLUoXDudrsA9tdL9i2VkJefEbx3zrFRt6swuibPVySPGNzsNvyshrRNZbSDnD",
|
||||
NetworkType::Stagenet, 24, 0x36E99D1DUL, "55AJ4jJBhV6JsoqrEsAazTLrJjg9SA1SFReLUoXDudrsA9tdL9i2VkJefEbx3zrFRt6swuibPVySPGNzsNvyshrRNZbSDnD",
|
||||
"57e0c2fef80a1d6adfa3189134009076ad0ddc4c4668709355cea98524e9fc36", "b94fafe59d5037e126557665f76cd3232504ebd82500e05bf25801d853d182bf"
|
||||
);
|
||||
|
||||
check(
|
||||
NetworkType::Stagenet, "5BQqg4HTWuN3j4NzBHTK31eTaygRXYxWRQW9dTD7qMuJSiVtskraSErXQ24FUBeifiV6NaQPmxLS559vbUT4xYUoF2fiGvH",
|
||||
NetworkType::Stagenet, 24, 0x16DF3958UL, "5BQqg4HTWuN3j4NzBHTK31eTaygRXYxWRQW9dTD7qMuJSiVtskraSErXQ24FUBeifiV6NaQPmxLS559vbUT4xYUoF2fiGvH",
|
||||
"fcd35a53cef9a1104ae556f01cee0cdff2f18f2f2f6bde8c833d5bd980fe8999", "be2b1142a046bfb5bb21e1f2a49bd1a7f46e1c18b009b218d5962f663938707c"
|
||||
);
|
||||
|
||||
check(
|
||||
NetworkType::Stagenet, "53CFYfjzcouW95hQ7AHvqS3GZ2UAAaRLKc1ymmhHATQTZxhtakpYcfjiRVzrRdxVZ5F8p61KSpPEmFu9DVRULRDkK4v1TCU",
|
||||
NetworkType::Stagenet, 24, 0xF17D6524UL, "53CFYfjzcouW95hQ7AHvqS3GZ2UAAaRLKc1ymmhHATQTZxhtakpYcfjiRVzrRdxVZ5F8p61KSpPEmFu9DVRULRDkK4v1TCU",
|
||||
"23fdd143264794ae367083791bb8fd0d8f719b27b7b858d15a2b67d6eddd60c5", "0ebafc1284ab1af7a5ff4ade682bcc54817a319a00eede591344855c420beba0"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user