add more configurations; add statistics webserver

pull/180/head
Blade Doyle 2 years ago
parent 7f1b849394
commit 50d206c150

@ -1 +1,2 @@
**/build
**/docker-compose

@ -70,7 +70,7 @@ jobs:
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j$(nproc)
- name: Build libuv
@ -132,7 +132,7 @@ jobs:
run: |
cd external/src/curl
autoreconf -fi
./configure --host=aarch64-linux-gnu --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts
./configure --host=aarch64-linux-gnu --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j$(nproc)
- name: Build libuv
@ -188,7 +188,7 @@ jobs:
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j$(nproc)
- name: Build libuv
@ -300,7 +300,7 @@ jobs:
run: |
cd external/src/curl
autoreconf -fi
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts
./configure --without-ssl --without-hyper --without-zlib --without-brotli --without-zstd --without-default-ssl-backend --without-ca-bundle --without-ca-path --without-ca-fallback --without-libpsl --without-libgsasl --without-librtmp --without-winidn --without-libidn2 --without-nghttp2 --without-ngtcp2 --without-nghttp3 --without-quiche --without-msh3 --without-zsh-functions-dir --without-fish-functions-dir --disable-ftp --disable-file --disable-ldap --disable-ldaps --disable-rtsp --disable-proxy --disable-dict --disable-telnet --disable-tftp --disable-pop3 --disable-imap --disable-smb --disable-smtp --disable-gopher --disable-mqtt --disable-manual --disable-ntlm --disable-ntlm-wb --disable-tls-srp --disable-unix-sockets --disable-cookies --disable-socketpair --disable-doh --disable-dateparse --disable-netrc --disable-progress-meter --disable-dnsshuffle --disable-hsts --disable-alt-svc --disable-ares
make -j3
- name: Build libuv

@ -12,7 +12,8 @@ RUN set -e && \
libsodium-dev \
libpgm-dev \
libnorm-dev \
libgss-dev
libgss-dev \
libcurl4-openssl-dev
ADD . /usr/src/p2pool
WORKDIR /usr/src/p2pool
@ -32,6 +33,7 @@ RUN set -e && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
libzmq5 \
libuv1 \
libcurl4 \
&& \
apt-get clean

@ -66,12 +66,12 @@ First you need to find a pool share. This share will stay in PPLNS window for 21
## Monero version support
Monero will undergo a network upgrade on July 16th, 2022 (block 2,668,888). In order to continue mining after that date, you must update both Monero and P2Pool software to the latest available versions as soon as they are released.
Monero will undergo a network upgrade on August 13th, 2022 (block 2,688,888). In order to continue mining after that date, you must update both Monero and P2Pool software to the latest available versions as soon as they are released.
|Monero protocol version|Required Monero software version|Required P2Pool version
|-|-|-|
|v14 (active until July 16th, 2022)|v0.17.3.0 or newer|v1.0 or newer
|v15, v16 (active after July 16th, 2022)|v0.18.0.0 or newer|v2.0 or newer
|v14 (active until August 13th, 2022)|v0.17.3.0 or newer|v1.0 or newer
|v15, v16 (active after August 13th, 2022)|v0.18.0.0 or newer|v2.2 or newer
## How to mine on P2Pool
@ -208,9 +208,11 @@ cmake ..
make -j$(nproc)
```
### Arch Linux [AUR](https://wiki.archlinux.org/title/Arch_User_Repository)
### [Arch Linux](https://archlinux.org/packages/community/x86_64/p2pool/)
Make the package: [p2pool-git](https://aur.archlinux.org/packages/p2pool-git/)
```
pacman -S p2pool
```
### [Nix/NixOS](https://nixos.org)

@ -1,42 +0,0 @@
##
# Monero Wallet Address for mining rewards (replace this with your own address)
WALLET_ADDRESS="44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg"
##
# p2pool settings
#
# Use p2pool-mini (uncomment to enable)
#P2POOL_MINI="--mini"
#
# Which port to listen for miner connections
P2POOL_STRATUM_PORT=3333
#
# How much logging - (Less) 0 - 6 (More)
P2POOL_LOGLEVEL=2
##
# Monero Node Settings
#
# Version of Monero to build. Must be v0.17.3.0 or later for p2pool support. "latest" pulls the most recent release tag
MONERO_GIT_TAG=latest
#
# Limit the size of the blockchain on disk (comment to disable)
PRUNE_NODE="--prune-blockchain"
#
# Other monerod commandline options
MONERO_EXTRA_OPTIONS=""
##
# Xmrig Miner Settings
#
# Submit shares at a lower fixed difficulty to show mining progress (comment to disable, edit to customize)
FIXED_MINING_DIFFICULTY="-u x+500000"
#
# set process priority (0 idle, 2 normal to 5 highest)
MINING_CPU_PRIORITY="--cpu-priority=2"
#
# Other XMRIG commandline options
# See: https://xmrig.com/docs/miner/command-line-options
XMRIG_EXTRA_OPTIONS=""

@ -8,6 +8,8 @@ Run your own <b>Monero Node + P2Pool + XMRig</b> in Docker
[Install Docker](https://docs.docker.com/engine/install/)
[Install Docker Compose](https://docs.docker.com/compose/install/)
Note: The docker compose plugin uses the command "docker compose" while the pip installed command is "docker-compose".
#### Clone the P2Pool project
```
git clone --recursive https://github.com/SChernykh/p2pool
@ -16,37 +18,38 @@ git clone --recursive https://github.com/SChernykh/p2pool
#### Configure your Monero address for mining rewards
```
cd p2pool/docker-compose
vi .env
./configure
```
**WALLET_ADDRESS** is the only setting that needs to be updated in that file
Make sure to set your own monero **Wallet Address**. The default is to donate mining to P2Pool development.
#### Build the docker containers
```
docker-compose build
docker compose build
```
#### Run the node, pool, and CPU miner
#### Run the node, pool, and CPU miner (or updated configuration)
```
docker-compose up
docker compose up
```
#### Optional
* Open ports 18080 (Monero p2p port) and 37889 (P2Pool p2p port) or 37888 (P2Pool-mini p2p port) in your firewall to ensure better connectivity. If you're mining from a computer behind NAT (like a router) you could consider forwarding the ports to your local machine
* An XMRig CPU miner is included by default, but you can connect additional miners to this same p2pool node using port 3333
* An XMRig CPU miner is included by default, but you can connect additional miners to this same p2pool node using port 3333 (or alternate if configured) when you set it as "exposed" in the configuration
* Configure your kernel for maximum mining performance: [XMRig RandomX Optimization Guide](https://xmrig.com/docs/miner/randomx-optimization-guide)
* Small miners can mine on p2pool-mini by editing the .env file
* Many optional configurations and customizations are available by running './configure'
#### Other usefull commands
* You can **run everythng in the background** by adding the "-d" argument to the "docker-compose up" command: ```docker-compose up -d```
* You can **stop everything** with CTRL-C or ```docker-compose down```
* You can **run everything in the background** by adding the "-d" argument to the "docker compose up" command: ```docker compose up -d```
* You can **stop everything** with CTRL-C or ```docker compose down```
* You can see logs when running in the background for with the "docker logs" command: ```docker logs -f p2pool-xmrig``` or ```docker logs -f p2pool-p2pool``` or ```docker logs -f p2pool-monero```
* You can pause mining with: ```docker-compose pause xmrig``` and resume mining with: ```docker-compose unpause xmrig```
* You can disable mining with: ```docker-compose stop xmrig``` and re-enable mining with: ```docker-compose start xmrig```
* You can pause mining with: ```docker compose pause xmrig``` and resume mining with: ```docker compose unpause xmrig```
* You can disable mining with: ```docker compose stop xmrig``` and re-enable mining with: ```docker compose start xmrig```
* You can view your Server Statistics using a web browser if you enabled that feature in the configuration at: http://localhost:3380 (or alternate port as configured)
#### Uninstall
Change to p2pool/docker-compose directory <br />
Stop and remove all containers: ```docker-compose down``` <br />
Stop and remove all containers: ```docker compose down``` <br />
Remove the p2pool data: ```docker volume rm p2pool``` <br />
Remove the monero data: ```docker volume rm monero```

@ -0,0 +1,14 @@
FROM python:3.10.5-bullseye
WORKDIR /app
ADD requirements.txt /app/
RUN /usr/local/bin/pip3 install -r requirements.txt
ADD configure.py /app/
ADD defaults /app/
ADD current_config.jinja2 /app/
ADD docker-compose.jinja2 /app/
ENTRYPOINT ["/usr/local/bin/python3"]
CMD ["/app/configure.py"]

@ -0,0 +1,951 @@
#!/bin/env python3
import sys
import time
import json
from jinja2 import Template
import npyscreen
# Help text in a box at the bottom of the screen
class HelpBoxBase(npyscreen.BoxTitle):
def splitlines(self):
all_help = []
for fld, msg in self.help_msgs.items():
if fld[-1] != ":":
fld += ":"
message = [m.lstrip() for m in msg if m != ""]
helpline = f"{fld:<28}{'. '.join(message)}"
all_help.append(helpline)
all_help.append("")
return all_help
def display_help_message(self, field):
self.set_values(
self.help_msgs.get(field, "No help availabe for {}".format(field))
)
self.clear()
self.display()
class P2PoolHelpBox(HelpBoxBase):
help_msgs = {
"Wallet Address:": [
"Your monero wallet address for receiving mining reqards",
"",
"Note: You have to use a primary wallet address for mining",
" Subaddresses and integrated addresses are not supported!",
],
"P2Pool Sidechain:": [
"Which P2Pool sidechain to mine on",
" use main for faster miners",
" use mini for slower miners",
],
"Enable Server Statistics": [
"Provide access to your P2Pool server statictics via a web interface",
],
"Statistics Port:": [
"Port number (or IP:Port) to expose web access to your P2Pool server",
"statictics",
],
"Expose Stratum Port": [
"Expose the P2Pool stratum port to your network so external miners",
"can connect",
"Note: You may choose to open this port in your hosts firewall and/or",
" router to allow miners outside your network to connect",
],
"Stratum Port:": [
"Port number (or IP:Port) to expose P2Pool stratum to your network to",
"allow external miners to connect",
"Note: You may choose to open this port in your hosts firewall and/or",
" router to allow miners outside your network to connect",
],
"P2Pool Log Level:": [
"Verbosity of the log; (Less) 0 - 6 (More)",
],
"Enable Autodiff": [
"Use automatic difficulty adjustment for miners connected to stratum",
],
"Enable Light Mode": [
"Don't allocate RandomX dataset, saves 2GB of RAM",
],
"Disable Cache": [
"Disable p2pool.cache (not recommended)",
],
"Additional P2Pool Options:": [
"Additional options to pass to p2pool commandline",
"",
"Note: Advanced - Only add options if you know what you are doing",
" See ouput of 'p2pool --help' for available options",
],
"Next": [
"Next configuration menu",
],
"Save": [
"Save current configuration and exit",
],
"Cancel": [
"Exit without saving",
],
}
class MoneroHelpBox(HelpBoxBase):
help_msgs = {
"Configure Monero Node": [
"Configure and run a Monero Node",
"",
"Note: You must either configure a local node or specify a public node",
],
"Monero Version:": [
"Version of Monero to build; 'latest' for the most recent release",
"",
"Note: Must be v0.17.3.0 or later for p2pool support",
" See: https://github.com/monero-project/monero/tags",
],
"Prune Blockchain": [
"Prune the Monero node to limit the size of the blockchain on disk",
],
"Monero Log Level:": [
"Verbosity of the log; (Less) 0 - 4 (More)",
"",
"Note: settings above 0 are very noisy",
],
"Expose RPC Port": [
"Expose restricted RPC API port to your network so external services",
"(wallets for example) can connect",
"Note: You may choose to open this port in your hosts firewall and/or",
" router to allow services outside your network to connect",
],
"RPC Port:": [
"TCP port to listen on for RPC connections",
],
"RPC Login:": [
"Specify username[:password] required to connect to the RPC API",
],
"Limit Data Rates": [
"Set a limit value for incoming and outgoing data transfer",
],
"Rate Limit Up:": [
"Set outgoing data transfer limit [kB/s]",
],
"Rate Limit Down:": [
"Set incoming data transfer limit [kB/s]",
],
"Sync Pruned Blocks": [
"Accept pruned blocks instead of pruning yourself to save",
"network transfer",
],
"Fast Block Sync": [
'Sync up most of the way by using embedded, "known" (old) block',
"hashes without calculating the block hash to verify the proof of work",
"",
"Note: Faster initial sync by trusting the monerod binary",
],
"Public Node:": [
"Public Monero Node to Use",
"",
"Note: The public node must have both Monero RPC and zmq-pub ports",
" available",
],
"Node Login:": [
"Specify username[:password] required to connect to public monero",
"node RPC API (if required)",
],
"Additional monerod Options:": [
"Additional options to pass to monerod commandline",
"",
"Note: Advanced - Only add options if you know what you are doing",
" See 'https://monerodocs.org/interacting/monerod-reference/'",
],
"Prev": [
"Previous configuration menu",
],
"Next": [
"Next configuration menu",
],
"Save": [
"Save current configuration and exit",
],
"Cancel": [
"Exit without saving",
],
}
class XMRigHelpBox(HelpBoxBase):
help_msgs = {
"Configure XMRig CPU Miner": [
"Configure and run an XMRig CPU Miner",
"",
"Note: You must either configure am XMRig CPU Miner or expose the",
" P2Pool stratum port and connect an external miner, or both",
],
"Username:": [
"Set a username for the miner",
],
"Use Fixed Difficulty": [
"Used a fixed minig difficulty",
"",
"Note: Allows you to see XMRig submitting shares below P2Pool threshold",
],
"Fixed Difficulty:": [
"Set a fixed mining difficulty",
"",
"Note: Allows you to see XMRig submitting shares below P2Pool threshold",
],
"CPU Use %:": [
"Maximum CPU threads count (in percentage) hint for autoconfig",
"Note: Applies to cores only. If you have HyperThreading enabled you",
" should divide this value by 2 (use 0-50%). Reference:",
" https://github.com/xmrig/xmrig/issues/1670#issuecomment-644433778",
],
"CPU Priority:": [
"Set process priority (0 idle, 2 normal to 5 highest)",
],
"Additional XMRig Options:": [
"Additional options to pass to xmrig",
"",
"Note: Advanced - Only add options if you know what you are doing",
" See 'https://xmrig.com/docs/miner/command-line-options'",
],
"Prev": [
"Previous configuration menu",
],
"Save": [
"Save current configuration and exit",
],
"Cancel": [
"Exit without saving",
],
}
##
# Custom (integer) values for title slider
class IntegerSlider(npyscreen.Slider):
def translate_value(self):
from_val = int(str(self.value).split(".")[0])
out_of_val = int(str(self.out_of).split(".")[0])
if from_val >= 1000:
from_val = str(from_val / 1000).split(".")[0] + "K"
out_of_val = str(out_of_val / 1000).split(".")[0] + "K"
return "{}/{}".format(from_val, out_of_val)
class TitleIntegerSlider(npyscreen.TitleSlider):
_entry_type = IntegerSlider
##
# Patched updateDependents for FormControlCheckbox
class PatchedFormControlCheckbox(npyscreen.FormControlCheckbox):
def updateDependents(self):
if self.value:
for w in self._visibleWhenSelected:
try:
w.fc_visible
except AttributeError:
w.fc_visible = {}
w.fc_visible[self.name] = True
for w in self._notVisibleWhenSelected:
try:
w.fc_visible
except AttributeError:
w.fc_visible = {}
w.fc_visible[self.name] = False
else:
for w in self._visibleWhenSelected:
try:
w.fc_visible
except AttributeError:
w.fc_visible = {}
w.fc_visible[self.name] = False
for w in self._notVisibleWhenSelected:
try:
w.fc_visible
except AttributeError:
w.fc_visible = {}
w.fc_visible[self.name] = True
for w in self._visibleWhenSelected + self._notVisibleWhenSelected:
w.hidden = False in w.fc_visible.values()
w.editable = not False in w.fc_visible.values()
self.parent.display()
def set_value(self, value):
self.value = value
self.display()
def display(self):
self.updateDependents()
super()
class PrevButton(npyscreen.Button):
def whenToggled(self):
self.value = False
self.parent.prev_form()
class NextButton(npyscreen.Button):
def whenToggled(self):
self.value = False
self.parent.next_form()
class SaveButton(npyscreen.Button):
def whenToggled(self):
self.find_parent_app().save_and_exit()
class CancelButton(npyscreen.Button):
def whenToggled(self):
self.find_parent_app().cancel_and_exit()
##
# Config Forms Base Class
class ConfigFormBase(npyscreen.FormBaseNew):
name_size = 20
indent = 5
current_config = None
defaults = None
ALLOW_RESIZE = False
def while_editing(self, arg):
self.help.display_help_message(arg.name)
self.display()
def get_default_config(self):
# Return defaults
if self.defaults is None:
with open("defaults") as defaults_file:
self.defaults = json.load(defaults_file)
return self.defaults
def get_current_config(self):
# Return current config
if self.current_config is None:
# Read current config
with open("/docker-compose/current_config") as current_config:
self.current_config = json.load(current_config)
return self.current_config
def reset_defaults(self, arg):
# Set config to default values
ok = npyscreen.notify_ok_cancel(
"Set current form values to defaults", title="Reset to Defaults"
)
if not ok:
return
defaults = self.get_default_config()
self.set_config(defaults)
##
# P2Pool Configuration Form
class P2PoolConfigForm(ConfigFormBase):
def create(self):
# Add Hot-Key Controls
self.add_handlers({"^D": self.reset_defaults})
# Add P2Pool Configuration
self.add(
npyscreen.TitleText,
name="## P2Pool Configuration",
editable=False,
begin_entry_at=50,
)
self.wallet_address = self.add(
npyscreen.TitleText,
name="Wallet Address:",
value="",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.nextrely += 1
self.sidechain = self.add(
npyscreen.TitleSelectOne,
name="P2Pool Sidechain:",
values=["main", "mini"],
scroll_exit=True,
max_height=2,
value=[
0,
],
begin_entry_at=self.name_size,
relx=self.indent,
)
self.nextrely += 1
self.enable_statistics = self.add(
PatchedFormControlCheckbox,
name="Enable Server Statistics",
value=True,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.statistics_port = self.add(
npyscreen.TitleText,
name="Statistics Port:",
value="3334",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.enable_statistics.addVisibleWhenSelected(self.statistics_port)
self.nextrely += 1
self.expose_stratum_port = self.add(
PatchedFormControlCheckbox,
name="Expose Stratum Port",
value=True,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.stratum_port = self.add(
npyscreen.TitleText,
name="Stratum Port:",
value="3333",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.expose_stratum_port.addVisibleWhenSelected(self.stratum_port)
self.nextrely += 1
self.p2pool_log_level = self.add(
TitleIntegerSlider,
name="P2Pool Log Level:",
out_of=6,
value=3,
lowest=0,
step=1,
width=43,
begin_entry_at=20,
label=True,
block_color=None,
relx=self.indent,
)
self.nextrely += 1
self.autodiff = self.add(
PatchedFormControlCheckbox,
name="Enable Autodiff",
value=True,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.nextrely += 1
self.light_mode = self.add(
PatchedFormControlCheckbox,
name="Enable Light Mode",
value=False,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.nextrely += 1
self.no_cache = self.add(
npyscreen.Checkbox,
name="Disable Cache",
value=False,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.nextrely += 1
self.p2pool_extra = self.add(
npyscreen.TitleText,
name="Additional P2Pool Options:",
value="",
begin_entry_at=self.name_size + 10,
relx=self.indent,
)
self.nextrely += 1
self.nextrely += 1
# Add "Next", "Save", and "Cancel" buttons
self.next_button = self.add(NextButton, name="Next", relx=1)
self.nextrely -= 1
self.save_button = self.add(SaveButton, name="Save", relx=8)
self.nextrely -= 1
self.cancel_button = self.add(CancelButton, name="Cancel", relx=15)
self.nextrely += 1
# Add Help Box
self.help = self.add(
P2PoolHelpBox,
name="Commands: ^D: Load Defaults - ^C: Exit Without Saving",
values=[""],
editable=False,
)
# Start with current config
self.set_config(self.get_current_config())
def next_form(self):
self.find_parent_app().switchForm("MONERO")
def set_config(self, config):
self.wallet_address.set_value(config["wallet_address"])
self.sidechain.set_value(config["sidechain"])
self.enable_statistics.set_value(config["enable_statistics"])
self.statistics_port.set_value(config["statistics_port"])
self.expose_stratum_port.set_value(config["expose_stratum_port"])
self.stratum_port.set_value(config["stratum_port"])
self.p2pool_log_level.set_value(config["p2pool_log_level"])
self.autodiff.set_value(config["enable_autodiff"])
self.light_mode.set_value(config["light_mode"])
self.no_cache.value = config["no_cache"]
self.p2pool_extra.set_value(config["p2pool_options"])
self.DISPLAY()
def get_config(self):
config = {
"wallet_address": self.wallet_address.value,
"sidechain": self.sidechain.value,
"enable_statistics": self.enable_statistics.value,
"statistics_port": self.statistics_port.value,
"expose_stratum_port": self.expose_stratum_port.value,
"stratum_port": self.stratum_port.value,
"p2pool_log_level": self.p2pool_log_level.value,
"enable_autodiff": self.autodiff.value,
"light_mode": self.light_mode.value,
"no_cache": self.no_cache.value,
"p2pool_options": self.p2pool_extra.value,
}
return config
##
# Monero Configuration Form
class MoneroConfigForm(ConfigFormBase):
def create(self):
# Add Hot-Key Controls
self.add_handlers({"^D": self.reset_defaults})
# Add Monero Configuration
self.add(
npyscreen.TitleText,
name="## Monero Node Configuration",
editable=False,
begin_entry_at=50,
)
self.configure_monero_node = self.add(
PatchedFormControlCheckbox,
name="Configure Monero Node",
value=True,
relx=self.indent,
)
self.nextrely += 1
self.monero_git_tag = self.add(
npyscreen.TitleText,
name="Monero Version:",
value="latest",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.monero_git_tag)
self.prune_node = self.add(
PatchedFormControlCheckbox,
name="Prune Blockchain",
value=True,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.prune_node)
self.monero_log_level = self.add(
TitleIntegerSlider,
name="Monero Log Level:",
out_of=4,
value=0,
lowest=0,
step=1,
width=43,
begin_entry_at=20,
label=True,
block_color=None,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.monero_log_level)
self.nextrely += 1
self.expose_rpc_port = self.add(
PatchedFormControlCheckbox,
name="Expose RPC Port",
value=False,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.expose_rpc_port)
self.rpc_port = self.add(
npyscreen.TitleText,
name="RPC Port:",
value="18081",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.rpc_port)
self.expose_rpc_port.addVisibleWhenSelected(self.rpc_port)
self.rpc_login = self.add(
npyscreen.TitleText,
name="RPC Login:",
value="",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.expose_rpc_port.addVisibleWhenSelected(self.rpc_login)
self.configure_monero_node.addVisibleWhenSelected(self.rpc_login)
self.nextrely += 1
self.limit_data_rates = self.add(
PatchedFormControlCheckbox,
name="Limit Data Rates",
value=False,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.limit_data_rates)
self.rate_limit_up = self.add(
npyscreen.TitleText,
name="Rate Limit Up:",
value="2048",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.limit_data_rates.addVisibleWhenSelected(self.rate_limit_up)
self.configure_monero_node.addVisibleWhenSelected(self.rate_limit_up)
self.rate_limit_down = self.add(
npyscreen.TitleText,
name="Rate Limit Down:",
value="8192",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.limit_data_rates.addVisibleWhenSelected(self.rate_limit_down)
self.configure_monero_node.addVisibleWhenSelected(self.rate_limit_down)
self.nextrely += 1
self.sync_pruned_blocks = self.add(
npyscreen.Checkbox,
name="Sync Pruned Blocks",
value=False,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.sync_pruned_blocks)
self.prune_node.addVisibleWhenSelected(self.sync_pruned_blocks)
self.fast_sync = self.add(
npyscreen.Checkbox,
name="Fast Block Sync",
value=False,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.fast_sync)
self.nextrely += 1
self.public_node = self.add(
npyscreen.TitleText,
name="Public Node:",
value="",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addInvisibleWhenSelected(self.public_node)
self.node_login = self.add(
npyscreen.TitleText,
name="Node Login:",
value="",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_monero_node.addInvisibleWhenSelected(self.node_login)
self.nextrely += 1
self.monero_extra = self.add(
npyscreen.TitleText,
name="Additional monerod Options:",
value="",
begin_entry_at=self.name_size + 10,
relx=self.indent,
)
self.configure_monero_node.addVisibleWhenSelected(self.monero_extra)
self.nextrely += 1
self.nextrely += 1
# Add "Prev", "Next", "Save", and "Cancel" buttons
self.prev_button = self.add(PrevButton, name="Prev", relx=1)
self.nextrely -= 1
self.next_button = self.add(NextButton, name="Next", relx=8)
self.nextrely -= 1
self.save_button = self.add(SaveButton, name="Save", relx=15)
self.nextrely -= 1
self.cancel_button = self.add(CancelButton, name="Cancel", relx=22)
self.nextrely += 1
# Add Help Box
self.help = self.add(
MoneroHelpBox,
name="Commands: ^D: Load Defaults - ^C: Exit Without Saving",
values=[""],
editable=False,
)
# Start with current config
self.set_config(self.get_current_config())
def prev_form(self):
self.find_parent_app().switchForm("MAIN")
def next_form(self):
self.find_parent_app().switchForm("XMRIG")
def set_config(self, config):
self.configure_monero_node.set_value(config["configure_monero"])
self.monero_git_tag.set_value(config["monero_version"])
self.prune_node.value = config["prune_blockchain"]
self.monero_log_level.set_value(config["monero_log_level"])
self.expose_rpc_port.set_value(config["expose_rpc_port"])
self.rpc_port.set_value(config["rpc_port"])
self.rpc_login.set_value(config["rpc_login"])
self.limit_data_rates.set_value(config["limit_data_rates"])
self.rate_limit_up.set_value(config["rate_limit_up"])
self.rate_limit_down.set_value(config["rate_limit_down"])
self.sync_pruned_blocks.value = config["sync_pruned_blocks"]
self.fast_sync.value = config["fast_sync"]
self.monero_extra.set_value(config["monero_options"])
self.public_node.set_value(config["public_monero_node"])
self.node_login.set_value(config["monero_node_login"])
self.DISPLAY()
def get_config(self):
config = {
"configure_monero": self.configure_monero_node.value,
"monero_version": self.monero_git_tag.value,
"prune_blockchain": self.prune_node.value,
"monero_log_level": self.monero_log_level.value,
"expose_rpc_port": self.expose_rpc_port.value,
"rpc_port": self.rpc_port.value,
"rpc_login": self.rpc_login.value,
"limit_data_rates": self.limit_data_rates.value,
"rate_limit_up": self.rate_limit_up.value,
"rate_limit_down": self.rate_limit_down.value,
"sync_pruned_blocks": self.sync_pruned_blocks.value,
"fast_sync": self.fast_sync.value,
"monero_options": self.monero_extra.value,
"public_monero_node": self.public_node.value,
"monero_node_login": self.node_login.value,
}
return config
##
# XMRig Configuration Form
class XMRigConfigForm(ConfigFormBase):
def create(self):
# Add Hot-Key Controls
self.add_handlers({"^D": self.reset_defaults})
# Hidden caryover from P2Pool form
self.autodiff = self.add(
PatchedFormControlCheckbox,
name="Enable Autodiff",
value=True,
begin_entry_at=self.name_size,
relx=self.indent,
)
self.autodiff.hide = True
self.autodiff.editable = False
self.nextrely -= 1
# Add XMRig Configuration
self.add(
npyscreen.TitleText,
name="## XMRig Miner Configuration",
editable=False,
begin_entry_at=50,
)
self.configure_xmrig_miner = self.add(
PatchedFormControlCheckbox,
name="Configure XMRig CPU Miner",
value=True,
relx=self.indent,
)
self.nextrely += 1
self.username = self.add(
npyscreen.TitleText,
name="Username:",
value="",
begin_entry_at=self.name_size,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.username)
self.nextrely += 1
self.use_fixed_difficulty = self.add(
PatchedFormControlCheckbox,
name="Use Fixed Difficulty",
value=True,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.use_fixed_difficulty)
self.autodiff.addInvisibleWhenSelected(self.use_fixed_difficulty)
self.fixed_difficulty = self.add(
TitleIntegerSlider,
name="Fixed Difficulty:",
out_of=2000000,
value=500000,
lowest=50000,
step=50000,
width=51,
begin_entry_at=20,
label=True,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.fixed_difficulty)
self.use_fixed_difficulty.addVisibleWhenSelected(self.fixed_difficulty)
self.autodiff.addInvisibleWhenSelected(self.fixed_difficulty)
self.nextrely += 1
self.cpu_threads = self.add(
TitleIntegerSlider,
name="CPU Use %:",
out_of=100,
value=100,
lowest=1,
step=10,
width=48,
begin_entry_at=20,
label=True,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.cpu_threads)
self.nextrely += 1
self.cpu_priority = self.add(
TitleIntegerSlider,
name="CPU Priority:",
out_of=5,
value=2,
lowest=0,
step=1,
width=48,
begin_entry_at=20,
label=True,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.cpu_priority)
self.nextrely += 1
self.xmrig_extra = self.add(
npyscreen.TitleText,
name="Additional XMRig Options:",
value="",
begin_entry_at=self.name_size + 10,
relx=self.indent,
)
self.configure_xmrig_miner.addVisibleWhenSelected(self.xmrig_extra)
self.nextrely += 1
self.nextrely += 1
# Add "Prev", "Save", and "Cancel" buttons
self.prev_button = self.add(PrevButton, name="Prev", relx=1)
self.nextrely -= 1
self.save_button = self.add(SaveButton, name="Save", relx=8)
self.nextrely -= 1
self.cancel_button = self.add(CancelButton, name="Cancel", relx=15)
self.nextrely += 1
# Add Help Box
self.help = self.add(
XMRigHelpBox,
name="Commands: ^D: Load Defaults - ^C: Exit Without Saving",
values=[""],
editable=False,
)
# Start with current config
self.set_config(self.get_current_config())
def prev_form(self):
self.find_parent_app().switchForm("MONERO")
def next_form(self):
self.find_parent_app().switchForm(None)
def beforeEditing(self):
# Cary autodiff value from P2Pool config form
enable_autodiff = self.find_parent_app().get_p2pool_config()["enable_autodiff"]
self.autodiff.set_value(enable_autodiff)
def set_config(self, config):
self.configure_xmrig_miner.set_value(config["configure_xmrig"])
self.username.set_value(config["xmrig_username"])
self.use_fixed_difficulty.set_value(config["use_fixed_difficulty"])
self.fixed_difficulty.set_value(config["fixed_difficulty"])
self.cpu_threads.set_value(config["cpu_percent"])
self.cpu_priority.set_value(config["cpu_priority"])
self.xmrig_extra.set_value(config["xmrig_options"])
self.DISPLAY()
def get_config(self):
config = {
"configure_xmrig": self.configure_xmrig_miner.value,
"xmrig_username": self.username.value,
"use_fixed_difficulty": self.use_fixed_difficulty.value,
"fixed_difficulty": self.fixed_difficulty.value,
"cpu_percent": self.cpu_threads.value,
"cpu_priority": self.cpu_priority.value,
"xmrig_options": self.xmrig_extra.value,
}
return config
##
# Our P2Pool configuration App
class ConfigApp(npyscreen.NPSAppManaged):
current_config = None
defaults = None
def onStart(self):
self.p2pool_form = self.addForm(
"MAIN",
P2PoolConfigForm,
name="P2Pool for docker-compose: P2Pool Configuration",
minimum_lines=35,
minimum_columns=80,
)
self.monero_form = self.addForm(
"MONERO",
MoneroConfigForm,
name="P2Pool for docker-compose: Monero Configuration",
minimum_lines=35,
minimum_columns=80,
)
self.xmrig_form = self.addForm(
"XMRIG",
XMRigConfigForm,
name="P2Pool for docker-compose: XMRig Configuration",
minimum_lines=35,
minimum_columns=80,
)
def get_p2pool_config(self):
return self.p2pool_form.get_config()
def get_config(self):
p2pool_config = self.p2pool_form.get_config()
monero_config = self.monero_form.get_config()
xmrig_config = self.xmrig_form.get_config()
return p2pool_config | monero_config | xmrig_config
def save_and_exit(self):
# Get config from all forms
config = self.get_config()
# Save "current config" values file
with open("current_config.jinja2", "r") as current_config:
template = current_config.read()
rendered = Template(template).render(config)
with open("/docker-compose/current_config", "w") as current_config:
current_config.write(rendered)
# Render and save docker-compose file
with open("docker-compose.jinja2", "r") as compose_file:
template = compose_file.read()
rendered = Template(template).render(config, trim_blocks=True)
with open("/docker-compose/docker-compose.yml", "w") as compose_file:
compose_file.write(rendered)
npyscreen.notify("Saved current settings", title="Saved")
self.switchForm(None)
self.saved = True
def cancel_and_exit(self):
self.switchForm(None)
self.saved = False
##
if __name__ == "__main__":
try:
time.sleep(1) # Give docker a second to initialize the terminal
App = ConfigApp()
App.run()
print("\n\n")
if App.saved:
print("Configuration Saved")
else:
print("Configuration Aborted")
sys.exit(1)
except KeyboardInterrupt:
print("Configuration Aborted")
sys.exit(1)

@ -0,0 +1,35 @@
{
"wallet_address": {{ wallet_address | tojson(indent=2) }},
"configure_monero": {{ configure_monero | tojson(indent=2) }},
"configure_xmrig": {{ configure_xmrig | tojson(indent=2) }},
"sidechain": {{ sidechain | tojson(indent=2) }},
"enable_statistics": {{ enable_statistics | tojson(indent=2) }},
"statistics_port": {{ statistics_port | tojson(indent=2) }},
"expose_stratum_port": {{ expose_stratum_port | tojson(indent=2) }},
"stratum_port": {{ stratum_port | tojson(indent=2) }},
"p2pool_log_level": {{ p2pool_log_level | int | tojson(indent=2) }},
"enable_autodiff": {{ enable_autodiff | tojson(indent=2) }},
"light_mode": {{ light_mode | tojson(indent=2) }},
"no_cache": {{ no_cache | tojson(indent=2) }},
"p2pool_options": {{ p2pool_options | tojson(indent=2) }},
"monero_version": {{ monero_version | tojson(indent=2) }},
"prune_blockchain": {{ prune_blockchain | tojson(indent=2) }},
"monero_log_level": {{ monero_log_level | int | tojson(indent=2) }},
"expose_rpc_port": {{ expose_rpc_port | tojson(indent=2) }},
"rpc_port": {{ rpc_port | tojson(indent=2) }},
"rpc_login": {{ rpc_login | tojson(indent=2) }},
"limit_data_rates": {{ limit_data_rates | tojson(indent=2) }},
"rate_limit_up": {{ rate_limit_up | tojson(indent=2) }},
"rate_limit_down": {{ rate_limit_down | tojson(indent=2) }},
"sync_pruned_blocks": {{ sync_pruned_blocks | tojson(indent=2) }},
"fast_sync": {{ fast_sync | tojson(indent=2) }},
"monero_options": {{ monero_options | tojson(indent=2) }},
"public_monero_node": {{ public_monero_node | tojson(indent=2) }},
"monero_node_login": {{ monero_node_login | tojson(indent=2) }},
"xmrig_username": {{ xmrig_username | tojson(indent=2) }},
"use_fixed_difficulty": {{ use_fixed_difficulty | tojson(indent=2) }},
"fixed_difficulty": {{ fixed_difficulty | int | tojson(indent=2) }},
"cpu_percent": {{ cpu_percent | int | tojson(indent=2) }},
"cpu_priority": {{ cpu_priority | int | tojson(indent=2) }},
"xmrig_options": {{ xmrig_options | tojson(indent=2) }}
}

@ -0,0 +1,35 @@
{
"wallet_address": "44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg",
"configure_monero": true,
"configure_xmrig": true,
"sidechain": [0],
"enable_statistics": true,
"statistics_port": "3380",
"expose_stratum_port": false,
"stratum_port": "3333",
"p2pool_log_level": 3,
"enable_autodiff": true,
"light_mode": false,
"no_cache": false,
"p2pool_options": "",
"monero_version": "latest",
"prune_blockchain": true,
"monero_log_level": 0,
"expose_rpc_port": false,
"rpc_port": "18081",
"rpc_login": "",
"limit_data_rates": false,
"rate_limit_up": "2048",
"rate_limit_down": "8192",
"sync_pruned_blocks": false,
"fast_sync": false,
"monero_options": "",
"public_monero_node": "",
"monero_node_login": "",
"xmrig_username": "p2pool",
"use_fixed_difficulty": true,
"fixed_difficulty": 500000,
"cpu_percent": 100,
"cpu_priority": 2,
"xmrig_options": ""
}

@ -0,0 +1,183 @@
---
version: '3.4'
networks:
p2pool:
driver: bridge
volumes:
p2pool:
name: p2pool
{% if configure_monero == True %}
monero:
name: monero
{% endif %}
services:
p2pool:
image: p2pool:latest
build: ../
container_name: p2pool-p2pool
networks:
- p2pool
privileged: true
ports:
{% if sidechain[0] == 0 %}
- 37888:37888/tcp
{% else %}
- 37889:37889/tcp
{% endif %}
{% if expose_stratum_port == True %}
- {{ stratum_port }}:3333/tcp
{% endif %}
volumes:
- p2pool:/home/p2pool/.p2pool:rw
- /dev/null:/home/p2pool/.p2pool/p2pool.log:rw
- /dev/hugepages:/dev/hugepages:rw
{% if configure_monero == True %}
depends_on:
monero:
condition: service_healthy
{% endif %}
restart: unless-stopped
command: >-
{% if configure_monero == True %}
--host monero
{% else %}
--host {{ public_monero_node }}
--rpc-login {{ monero_node_login }}
{% endif %}
--wallet {{ wallet_address }}
--loglevel {{ p2pool_log_level | int }}
{% if sidechain[0] == 1 %}
--mini
{% endif %}
{% if enable_autodiff == False %}
--no-autodiff
{% endif %}
{% if enable_statistics == True %}
--local-api
--data-api /home/p2pool/.p2pool
{% endif %}
{% if light_mode == True %}
--light-mode
{% endif %}
{% if no_cache == True %}
--no-cache
{% endif %}
{% if rpc_login != "" %}
--rpc-login {{ rpc_login }}
{% endif %}
{% if p2pool_options != "" %}
{{ p2pool_options }}
{% endif %}
{% if enable_statistics == True %}
statistics:
image: statistics:latest
build:
context: statistics
container_name: p2pool-statistics
networks:
- p2pool
ports:
- {{ statistics_port }}:80/tcp
volumes:
- p2pool:/data:r
depends_on:
- p2pool
restart: unless-stopped
{% endif %}
{% if configure_monero == True %}
monero:
image: monero:latest
build:
context: monero
args:
- MONERO_GIT_TAG={{ monero_version }}
container_name: p2pool-monero
networks:
- p2pool
ports:
- 18080:18080/tcp
{% if expose_rpc_port == True %}
- {{ rpc_port }}:18081/tcp
{% endif %}
volumes:
- monero:/home/monero/.bitmonero:rw
- /dev/null:/home/monero/.bitmonero/bitmonero.log:rw
- /dev/hugepages:/dev/hugepages:rw
restart: unless-stopped
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "18081"]
interval: 2s
timeout: 1s
start_period: 10s
command: >-
--zmq-pub tcp://0.0.0.0:18083
--disable-dns-checkpoints
--enable-dns-blocklist
--non-interactive
--p2p-bind-ip=0.0.0.0
--p2p-bind-port=18080
--rpc-bind-ip=0.0.0.0
--rpc-bind-port=18081
--restricted-rpc
--confirm-external-bind
--log-level={{ monero_log_level | int }}
{% if prune_blockchain == True %}
--prune-blockchain
{% if sync_pruned_blocks == True %}
--sync-pruned-blocks
{% endif %}
{% endif %}
{% if rpc_login != "" %}
--rpc-login {{ rpc_login }}
{% endif %}
{% if limit_data_rates == True %}
--limit-rate-up {{ rate_limit_up }}
--limit-rate-down {{ rate_limit_down }}
{% endif %}
{% if fast_sync == True %}
--fast-block-sync=1
{% else %}
--fast-block-sync=0
{% endif %}
{% if monero_options != "" %}
{{ monero_options }}
{% endif %}
{% endif %}
{% if configure_xmrig == True %}
xmrig:
image: xmrig:latest
build: xmrig
container_name: p2pool-xmrig
networks:
- p2pool
privileged: true
volumes:
- /dev:/dev:ro
- /lib/modules:/lib/modules:ro
- /dev/hugepages:/dev/hugepages:rw
depends_on:
- p2pool
restart: unless-stopped
command: >-
--randomx-1gb-pages
-o p2pool:3333
{% if enable_autodiff == False and use_fixed_difficulty == True %}
-u {{ xmrig_username }}+{{ fixed_difficulty | int }}
{% else %}
-u {{ xmrig_username }}
{% endif %}
--cpu-max-threads-hint={{ cpu_percent | int }}
--cpu-priority={{ cpu_priority | int }}
{% if xmrig_options != "" %}
{{ xmrig_options }}
{% endif %}
{% endif %}

@ -0,0 +1,3 @@
npyscreen==4.10.5
MarkupSafe==2.0.1
jinja2==2.11.3

@ -0,0 +1,45 @@
#!/bin/bash
echo ""
echo ""
echo "Verifying Requirements:"
DOCKER_VER=$(docker version -f "{{.Server.Version}}" 2> /dev/null)
if [ -z "$DOCKER_VER" ]; then
echo "Docker not found; install it: https://docs.docker.com/engine/install/"
exit 1
fi
if [ "$(echo "$DOCKER_VER"| cut -d'.' -f 1)" -ge 19 ] && \
[ "$(echo "$DOCKER_VER"| cut -d'.' -f 2)" -ge 0 ] && \
[ "$(echo "$DOCKER_VER"| cut -d'.' -f 3)" -ge 3 ]; then
echo "Docker Found; OK"
else
echo "Docker version less than 19.0.3; upgrade it: https://docs.docker.com/engine/install/"
exit 1
fi
docker compose version 2>&1 > /dev/null
COMPOSE_PLUGIN_RC=$?
docker-compose --version 2>&1 > /dev/null
COMPOSE_CLI_RC=$?
if [ "$COMPOSE_PLUGIN_RC" -eq 0 ] || [ "$COMPOSE_CLI_RC" -eq 0 ]; then
echo "Docker Compose found; OK"
if [ "$COMPOSE_PLUGIN_RC" -eq 0 ]; then
COMPOSE_COMMAND="docker compose"
else
COMPOSE_COMMAND="docker-compose"
fi
else
echo "Docker Compose not found; install it: https://docs.docker.com/compose/install/compose-plugin/"
exit 1
fi
echo ""
echo ""
echo "Building and Running P2Pool docker-compose Configuration"
docker build -t p2pool_config:latest cfg
docker run -it --rm -v $PWD:/docker-compose --user $(id -u):$(id -g) p2pool_config:latest
CONFIGURE_RC=$?
echo ""
echo ""
if [ "$CONFIGURE_RC" -eq 0 ]; then
echo "P2Pool is configured. Start the project with: $COMPOSE_COMMAND up --build -d"
else
exit 1
fi

@ -0,0 +1,37 @@
{
"wallet_address": "44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg",
"configure_monero": true,
"configure_xmrig": true,
"sidechain": [
0
],
"enable_statistics": true,
"statistics_port": "3380",
"expose_stratum_port": false,
"stratum_port": "3333",
"p2pool_log_level": 3,
"enable_autodiff": true,
"light_mode": false,
"no_cache": false,
"p2pool_options": "",
"monero_version": "latest",
"prune_blockchain": true,
"monero_log_level": 0,
"expose_rpc_port": false,
"rpc_port": "18081",
"rpc_login": "",
"limit_data_rates": false,
"rate_limit_up": "2048",
"rate_limit_down": "8192",
"sync_pruned_blocks": false,
"fast_sync": false,
"monero_options": "",
"public_monero_node": "",
"monero_node_login": "",
"xmrig_username": "p2pool",
"use_fixed_difficulty": true,
"fixed_difficulty": 500000,
"cpu_percent": 100,
"cpu_priority": 2,
"xmrig_options": ""
}

@ -8,26 +8,93 @@ networks:
volumes:
p2pool:
name: p2pool
monero:
name: monero
services:
p2pool:
image: p2pool:latest
build: ../
container_name: p2pool-p2pool
networks:
- p2pool
privileged: true
ports:
- 37888:37888/tcp
volumes:
- p2pool:/home/p2pool/.p2pool:rw
- /dev/null:/home/p2pool/.p2pool/p2pool.log:rw
- /dev/hugepages:/dev/hugepages:rw
depends_on:
monero:
condition: service_healthy
restart: unless-stopped
command: >-
--host monero
--wallet 44MnN1f3Eto8DZYUWuE5XZNUtE3vcRzt2j6PzqWpPau34e6Cf4fAxt6X2MBmrm6F9YMEiMNjN6W4Shn4pLcfNAja621jwyg
--loglevel 3
--local-api
--data-api /home/p2pool/.p2pool
statistics:
image: statistics:latest
build:
context: statistics
container_name: p2pool-statistics
networks:
- p2pool
ports:
- 3380:80/tcp
volumes:
- p2pool:/data:r
depends_on:
- p2pool
restart: unless-stopped
monero:
image: monero:latest
build:
context: monero
args:
- MONERO_GIT_TAG=${MONERO_GIT_TAG}
- MONERO_GIT_TAG=latest
container_name: p2pool-monero
networks:
- p2pool
ports:
- 18080:18080/tcp
volumes:
- monero:/home/monero/.bitmonero:rw
- /dev/null:/home/monero/.bitmonero/bitmonero.log:rw
- /dev/hugepages:/dev/hugepages:rw
restart: unless-stopped
healthcheck:
test: ["CMD", "nc", "-z", "localhost", "18081"]
interval: 2s
timeout: 1s
start_period: 10s
command: >-
--zmq-pub tcp://0.0.0.0:18083
--disable-dns-checkpoints
@ -38,31 +105,20 @@ services:
--rpc-bind-ip=0.0.0.0
--rpc-bind-port=18081
--confirm-external-bind
${PRUNE_NODE}
${MONERO_EXTRA_OPTIONS}
--log-level=0
--prune-blockchain
--fast-block-sync=0
p2pool:
image: p2pool:latest
build: ../
container_name: p2pool-p2pool
networks:
- p2pool
ports:
- ${P2POOL_STRATUM_PORT}:3333/tcp
- 37888:37888/tcp
- 37889:37889/tcp
volumes:
- p2pool:/home/p2pool/.p2pool:rw
- /dev/null:/home/p2pool/.p2pool/p2pool.log:rw
- /dev/hugepages:/dev/hugepages:rw
depends_on:
- monero
restart: unless-stopped
command: >-
--host monero
--wallet ${WALLET_ADDRESS}
--loglevel ${P2POOL_LOGLEVEL}
${P2POOL_MINI}
xmrig:
image: xmrig:latest
@ -79,8 +135,11 @@ services:
- p2pool
restart: unless-stopped
command: >-
${FIXED_MINING_DIFFICULTY}
${MINING_CPU_PRIORITY}
${XMRIG_EXTRA_OPTIONS}
--randomx-1gb-pages
-o p2pool:3333
-u p2pool
--cpu-max-threads-hint=100
--cpu-priority=2

@ -62,8 +62,10 @@ RUN set -e && \
apt-get update -q -y --no-install-recommends && \
DEBIAN_FRONTEND="noninteractive" apt-get install -q -y --no-install-recommends \
libgssapi-krb5-2 \
netcat \
&& \
apt-get clean
apt-get clean && \
rm -rf /var/lib/apt
RUN groupadd -r monero -g 1000 && \
useradd -u 1000 -r -g monero -s /sbin/nologin -c "monero node user" monero

@ -0,0 +1,8 @@
FROM python:slim
COPY app /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["/app/p2pool_statistics.py"]

@ -0,0 +1,78 @@
#!/usr/bin/env python3
import json
from datetime import datetime
from prefixed import Float
import humanfriendly
from flask import Flask, render_template
app = Flask(__name__)
##
# Add some custom jinja filters
def timeago(value):
"""Format a date time to human friendly time ago"""
if value is None:
return ""
if type(value) is int:
dt = datetime.fromtimestamp(value).replace(microsecond=0)
now = datetime.now().replace(microsecond=0)
return humanfriendly.format_timespan(now - dt)
app.jinja_env.filters["timeago"] = timeago
def human_numbers(value):
"""Format a number in human readable format"""
if value is None:
return ""
return "{:!.3h}".format(Float(value))
app.jinja_env.filters["humanize"] = human_numbers
##
# Get Pool Instance Birth Date
def birthdate():
try:
with open("/data/p2pool.blocks") as reader:
first_block = reader.readline().rstrip()
bday_ts = int(first_block.split(" ")[0])
bday = timeago(bday_ts)
return bday
except Exception as e:
return "unknown time"
##
# The App Routes
@app.route("/")
def render():
try:
my_bday = birthdate()
with open("/data/stats_mod", "r") as reader:
stats_mod = json.loads(reader.read())
with open("/data/pool/stats", "r") as reader:
pool_stats = json.loads(reader.read())
with open("/data/network/stats", "r") as reader:
network_stats = json.loads(reader.read())
with open("/data/local/stats", "r") as reader:
local_stats = json.loads(reader.read())
return render_template(
"index.html",
my_bday=my_bday,
stats_mod=stats_mod,
pool_stats=pool_stats,
network_stats=network_stats,
local_stats=local_stats,
)
except Exception as e:
return render_template("oops.html", error=str(e))
##
# main()
if __name__ == "__main__":
app.run(debug=False, host="0.0.0.0", port=80)

@ -0,0 +1,3 @@
flask
prefixed
humanfriendly

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

@ -0,0 +1,171 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
<title>Monero P2Pool Server Statistics</title>
</head>
<body style="font-size:85%;">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
crossorigin="anonymous">
</script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous">
</script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous">
</script>
<div style="text-align:center;">
<img src="{{url_for('static', filename='monero-symbol-480.png')}}" width="75px" height="75px" alt="Monero"/>
<h1 style="color: #FFA500;">P2Pool Server Statistics</h1>
</div>
<div class="card-group">
<div class="col-sm-4 grid-margin stretch-card">
<div class="card">
<div class="card-header text-black mb-1 pb-1" style="background-color: #FFA500;"><h2>Local Pool</h2></div>
<div class="card-body mb-0 pb-0">
<h6 class="card-subtitle text-muted" style="font-size:80%;">(note: stats reset on restart)</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Hashrate 15 Minutes</td>
<td>{{ local_stats["hashrate_15m"]|humanize }}H/s</td>
</tr>
<tr>
<td>Hashrate 1 Hour</td>
<td>{{ local_stats["hashrate_1h"]|humanize }}H/s</td>
</tr>
<tr>
<td>Hashrate 24 Hours</td>
<td>{{ local_stats["hashrate_24h"]|humanize }}H/s</td>
</tr>
<tr>
<td>Current Effort</td>
<td>{{ local_stats["current_effort"] }}%</td>
</tr>
<tr>
<td>Miner Connections</td>
<td>{{ local_stats["incoming_connections"] }}<td>
</tr>
<tr>
<td>Total Hashes</td>
<td>{{ local_stats["total_hashes"]|humanize }}</td>
</tr>
<tr>
<td>Shares Found</td>
<td>{{ local_stats["shares_found"] }}</td>
</tr>
<tr>
<td>Average Effort</td>
<td>{{ local_stats["average_effort"] }}%</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-sm-4 grid-margin stretch-card">
<div class="card">
<div class="card-header text-black mb-1 pb-1" style="background-color: #FFA500;"><h2>Global Pool</h2></div>
<div class="card-body mb-0 pb-0">
<h6 class="card-subtitle text-muted" style="font-size:80%;">&nbsp;</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Hash Rate</td>
<td>{{ pool_stats["pool_statistics"]["hashRate"]|humanize }}H/s</td>
</tr>
<tr>
<td>Round Hashes</td>
<td>{{ stats_mod["pool"]["roundHashes"]|humanize }}</td>
</tr>
<tr>
<td>Last Block Found</td>
<td>{{ pool_stats["pool_statistics"]["lastBlockFound"] }} <br>
{{ pool_stats["pool_statistics"]["lastBlockFoundTime"]|timeago }} ago</td>
</tr>
<tr>
<td>Payout Method</td>
<td>{{ pool_stats["pool_list"]|join(',') }}<br>2160 block window (~6 hours)</td>
</tr>
</tbody>
</table>
</div>
<h6 class="card-subtitle text-muted" style="font-size:80%;">(since instance birth: {{ my_bday }} ago)</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Total Hashes</td>
<td>{{ pool_stats["pool_statistics"]["totalHashes"]|humanize }}</td>
</tr>
<tr>
<td>Blocks Found</td>
<td>{{ pool_stats["pool_statistics"]["totalBlocksFound"] }}</td>
</tr>
<tr>
<td>Known Miners</td>
<td>{{ pool_stats["pool_statistics"]["miners"] }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-sm-4 grid-margin stretch-card">
<div class="card">
<div class="card-header text-black mb-1 pb-1" style="background-color: #FFA500;"><h2>Monero Network</h2></div>
<div class="card-body mb-0 pb-0">
<h6 class="card-subtitle text-muted" style="font-size:80%;">&nbsp;</h6>
<div class="table-responsive table-hover table-condensed table-striped">
<table class="table">
<tbody>
<tr>
<td>Height</td>
<td>{{ network_stats["height"] }}<br>
{{ network_stats["timestamp"]|timeago }} ago</td>
</tr>
<tr>
<td>Difficulty</td>
<td>{{ network_stats["difficulty"]|humanize }}</td>
</tr>
<tr>
<td>Reward</td>
<td>0.{{ network_stats["reward"] }} Monero</td>
</tr>
<tr>
<td>Head&nbsp;Hash</td>
<td>{{ network_stats["hash"] }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
window.setInterval('refresh()', 30000); // Call refresh function every 30000 milliseconds (30 seconds).
function refresh() {
window .location.reload();
}
</script>
</body>
</html>

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Oops</title>
</head>
<body>
<h2>Ooops, something went wrong:</h2>
{{ error }}
(Maybe you need to wait a few minutes for p2pool to start and sync for the first time?)
</body>
</html>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -1 +1 @@
Subproject commit 7167cf1ce6e5e9b240967e0d96148966a24bbdb0
Subproject commit 256d27c5c5a8c9f22b9711b71b24f1978fdb66cc

@ -1 +1 @@
Subproject commit 26362337a9bf3c3e7e7887338fa35b596a2bae59
Subproject commit 7742eb369387b3c7caba8483f1dd7e8d89915042

2
external/src/curl vendored

@ -1 +1 @@
Subproject commit 2bd75e5686b2e6ff3824c4dfb2b6ec86b60f454c
Subproject commit e5926fe5f91ae5673c7d5e31e484aed4188581f7

@ -1 +1 @@
Subproject commit 99ab53e9980dc0b2fb31b4615ce754bf1f35826f
Subproject commit 1a91b51976a1adc6972081faa78b6b70022254d3

@ -50,6 +50,8 @@ BlockTemplate::BlockTemplate(p2pool* pool)
, m_difficulty{}
, m_seedHash{}
, m_timestamp(0)
, m_txkeyPub{}
, m_txkeySec{}
, m_poolBlockTemplate(new PoolBlock())
, m_finalReward(0)
{
@ -73,8 +75,6 @@ BlockTemplate::BlockTemplate(p2pool* pool)
#if TEST_MEMPOOL_PICKING_ALGORITHM
m_knapsack.reserve(512 * 309375);
#endif
update_tx_keys();
}
BlockTemplate::~BlockTemplate()
@ -197,6 +197,8 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, Wallet
*this = *m_oldTemplates[id % array_size(&BlockTemplate::m_oldTemplates)];
};
get_tx_keys(m_txkeyPub, m_txkeySec, miner_wallet->spend_public_key(), data.prev_id);
m_height = data.height;
m_difficulty = data.difficulty;
m_seedHash = data.seed_hash;
@ -1057,13 +1059,6 @@ std::vector<uint8_t> BlockTemplate::get_block_template_blob(uint32_t template_id
return m_blockTemplateBlob;
}
void BlockTemplate::update_tx_keys()
{
WriteLock lock(m_lock);
generate_keys(m_txkeyPub, m_txkeySec);
}
void BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, uint32_t extra_nonce)
{
WriteLock lock(m_lock);

@ -47,7 +47,6 @@ public:
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;
void update_tx_keys();
FORCEINLINE uint64_t height() const { return m_height; }
FORCEINLINE difficulty_type difficulty() const { return m_difficulty; }

@ -232,7 +232,6 @@ void ConsoleCommands::run()
if (std::cin.eof()) {
LOGINFO(1, "EOF, stopping");
do_exit(m_pool, nullptr);
return;
}

@ -24,6 +24,10 @@ extern "C" {
#include "crypto-ops.h"
}
// l = 2^252 + 27742317777372353535851937790883648493.
// l fits 15 times in 32 bytes (iow, 15 l is the highest multiple of l that fits in 32 bytes)
static constexpr uint8_t limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 };
namespace p2pool {
namespace {
@ -79,10 +83,6 @@ static FORCEINLINE bool less32(const uint8_t* k0, const uint8_t* k1)
// cppcheck-suppress constParameter
void generate_keys(hash& pub, hash& sec)
{
// l = 2^252 + 27742317777372353535851937790883648493.
// l fits 15 times in 32 bytes (iow, 15 l is the highest multiple of l that fits in 32 bytes)
static constexpr uint8_t limit[32] = { 0xe3, 0x6a, 0x67, 0x72, 0x8b, 0xce, 0x13, 0x29, 0x8f, 0x30, 0x82, 0x8c, 0x0b, 0xa4, 0x10, 0x39, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0 };
do {
do { randomBytes(sec.h); } while (!less32(sec.h, limit));
sc_reduce32(sec.h);
@ -93,6 +93,29 @@ void generate_keys(hash& pub, hash& sec)
ge_p3_tobytes(pub.h, &point);
}
void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len)
{
uint32_t counter = 0;
do {
do {
++counter;
keccak_custom([entropy, len, counter](int offset)
{
if (offset < static_cast<int>(len)) {
return entropy[offset];
}
return static_cast<uint8_t>(counter >> ((offset - len) * 8));
}, static_cast<int>(len + sizeof(counter)), sec.h, HASH_SIZE);
} while (!less32(sec.h, limit));
sc_reduce32(sec.h);
} while (!sc_isnonzero(sec.h));
ge_p3 point;
ge_scalarmult_base(&point, sec.h);
ge_p3_tobytes(pub.h, &point);
}
bool check_keys(const hash& pub, const hash& sec)
{
// From ge_scalarmult_base's comment: "preconditions a[31] <= 127"
@ -226,12 +249,45 @@ public:
return true;
}
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
{
std::array<uint8_t, HASH_SIZE * 2> index;
memcpy(index.data(), wallet_spend_key.h, HASH_SIZE);
memcpy(index.data() + HASH_SIZE, monero_block_id.h, HASH_SIZE);
{
MutexLock lock(m);
auto it = tx_keys.find(index);
if (it != tx_keys.end()) {
pub = it->second.first;
sec = it->second.second;
return;
}
}
static constexpr char domain[] = "tx_secret_key";
static constexpr size_t N = sizeof(domain) - 1;
uint8_t entropy[N + HASH_SIZE * 2];
memcpy(entropy, domain, N);
memcpy(entropy + N, wallet_spend_key.h, HASH_SIZE);
memcpy(entropy + N + HASH_SIZE, monero_block_id.h, HASH_SIZE);
generate_keys_deterministic(pub, sec, entropy, sizeof(entropy));
{
MutexLock lock(m);
tx_keys.emplace(index, std::pair<hash, hash>(pub, sec));
}
}
void clear()
{
MutexLock lock(m);
derivations.clear();
public_keys.clear();
tx_keys.clear();
}
private:
@ -245,6 +301,7 @@ private:
uv_mutex_t m;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, DerivationEntry> derivations;
unordered_map<std::array<uint8_t, HASH_SIZE * 2 + sizeof(size_t)>, hash> public_keys;
unordered_map<std::array<uint8_t, HASH_SIZE * 2>, std::pair<hash, hash>> tx_keys;
};
static Cache* cache = nullptr;
@ -259,6 +316,11 @@ bool derive_public_key(const hash& derivation, size_t output_index, const hash&
return cache->get_public_key(derivation, output_index, base, derived_key);
}
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id)
{
cache->get_tx_keys(pub, sec, wallet_spend_key, monero_block_id);
}
void derive_view_tag(const hash& derivation, size_t output_index, uint8_t& view_tag)
{
constexpr uint8_t salt[] = "view_tag";

@ -20,6 +20,8 @@
namespace p2pool {
void generate_keys(hash& pub, hash& sec);
void generate_keys_deterministic(hash& pub, hash& sec, const uint8_t* entropy, size_t len);
void get_tx_keys(hash& pub, hash& sec, const hash& wallet_spend_key, const hash& monero_block_id);
bool check_keys(const hash& pub, const hash& sec);
bool generate_key_derivation(const hash& key1, const hash& key2, size_t output_index, hash& derivation, uint8_t& view_tag);
bool derive_public_key(const hash& derivation, size_t output_index, const hash& base, hash& derived_key);

@ -104,7 +104,7 @@ struct parse_wrapper<T, difficulty_type>
if (!from_hex(s[i], d)) {
return false;
}
out_value.hi = (out_value.hi << 4) || (out_value.lo >> 60);
out_value.hi = (out_value.hi << 4) | (out_value.lo >> 60);
out_value.lo = (out_value.lo << 4) | d;
}

@ -80,8 +80,9 @@ void Miner::print_status()
const uint64_t hr = (dt > 0.0) ? static_cast<uint64_t>(hash_count / dt) : 0;
LOGINFO(0, "status" <<
"\nThreads = " << m_threads <<
"\nHashrate = " << log::Hashrate(hr)
"\nThreads = " << m_threads <<
"\nHashrate = " << log::Hashrate(hr) <<
"\nShares found = " << m_sharesFound.load()
);
}
@ -111,7 +112,7 @@ void Miner::on_block(const BlockTemplate& block)
const double time_running = static_cast<double>(duration_cast<milliseconds>(cur_ts - m_startTimestamp).count()) / 1e3;
s << "{\"current_hashrate\":" << hr
<< ",\"total_hashes\":" << m_totalHashes
<< ",\"total_hashes\":" << m_totalHashes.load()
<< ",\"time_running\":" << time_running
<< ",\"shares_found\":" << m_sharesFound.load()
<< ",\"block_reward_share_percent\":" << block_reward_share_percent
@ -121,6 +122,12 @@ void Miner::on_block(const BlockTemplate& block)
}
}
void Miner::reset_share_counters()
{
m_totalHashes = 0;
m_sharesFound = 0;
}
void Miner::run(void* data)
{
WorkerData* d = static_cast<WorkerData*>(data);
@ -198,7 +205,6 @@ void Miner::run(WorkerData* data)
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");
m_pool->submit_block_async(j.m_templateId, j.m_nonce, j.m_extraNonce);
m_pool->block_template().update_tx_keys();
}
if (j.m_sidechainDiff.check_pow(h)) {

@ -33,6 +33,7 @@ public:
void print_status();
void on_block(const BlockTemplate& block);
void reset_share_counters();
private:
static void run(void* data);
@ -57,7 +58,7 @@ private:
std::chrono::high_resolution_clock::time_point m_nonceTimestamp;
const uint32_t m_extraNonce;
uint64_t m_totalHashes;
std::atomic<uint64_t> m_totalHashes;
std::atomic<uint32_t> m_sharesFound;
struct Job

@ -1348,6 +1348,15 @@ void p2pool::stop_mining()
m_miner = nullptr;
}
}
void p2pool::reset_miner()
{
MutexLock lock(m_minerLock);
if (m_miner) {
m_miner->reset_share_counters();
}
}
#endif
static void on_signal(uv_signal_t* handle, int signum)

@ -91,6 +91,7 @@ public:
#ifdef WITH_RANDOMX
void start_mining(uint32_t threads);
void stop_mining();
void reset_miner();
#endif
uint64_t zmq_last_active() const { return m_zmqLastActive; }

@ -101,7 +101,7 @@ void p2pool_api::on_stop()
uv_close(reinterpret_cast<uv_handle_t*>(&m_dumpToFileAsync), nullptr);
}
void p2pool_api::dump_to_file_async_internal(const Category& category, const char* filename, DumpFileCallbackBase&& callback)
void p2pool_api::dump_to_file_async_internal(Category category, const char* filename, DumpFileCallbackBase&& callback)
{
std::vector<char> buf(16384);
log::Stream s(buf.data(), buf.size());

@ -38,7 +38,7 @@ public:
void on_stop();
template<typename T>
void set(const Category& category, const char* filename, T&& callback) { dump_to_file_async_internal(category, filename, DumpFileCallback<T>(std::move(callback))); }
void set(Category category, const char* filename, T&& callback) { dump_to_file_async_internal(category, filename, DumpFileCallback<T>(std::move(callback))); }
private:
void create_dir(const std::string& path);
@ -72,7 +72,7 @@ private:
T m_callback;
};
void dump_to_file_async_internal(const Category& category, const char* filename, DumpFileCallbackBase&& callback);
void dump_to_file_async_internal(Category category, const char* filename, DumpFileCallbackBase&& callback);
void dump_to_file();
static void on_fs_open(uv_fs_t* req);
static void on_fs_write(uv_fs_t* req);

@ -229,6 +229,15 @@ int PoolBlock::deserialize(const uint8_t* data, size_t size, SideChain& sidechai
return __LINE__;
}
// Enforce deterministic tx keys starting from v15
if (m_majorVersion >= HARDFORK_VIEW_TAGS_VERSION) {
hash pub, sec;
get_tx_keys(pub, sec, spend_pub_key, m_prevId);
if ((pub != m_txkeyPub) || (sec != m_txkeySec)) {
return __LINE__;
}
}
READ_BUF(m_parent.h, HASH_SIZE);
uint64_t num_uncles;

@ -440,7 +440,9 @@ bool RandomX_Hasher_RPC::calculate(const void* data_ptr, size_t size, uint64_t h
volatile int result = 0;
volatile bool done = false;
JSONRPCRequest::call(m_pool->params().m_host, m_pool->params().m_rpcPort, buf, m_pool->params().m_rpcLogin,
const Params& params = m_pool->params();
JSONRPCRequest::call(params.m_host, params.m_rpcPort, buf, params.m_rpcLogin,
[&result, &h](const char* data, size_t size)
{
rapidjson::Document doc;

@ -1475,9 +1475,14 @@ void SideChain::update_chain_tip(PoolBlock* block)
m_pool->update_block_template_async();
// Reset stratum share counters when switching to an alternative chain to avoid confusion
StratumServer* s = m_pool->stratum_server();
if (s && is_alternative) {
s->reset_share_counters();
if (is_alternative) {
StratumServer* s = m_pool->stratum_server();
if (s) {
s->reset_share_counters();
}
#ifdef WITH_RANDOMX
m_pool->reset_miner();
#endif
LOGINFO(0, log::LightCyan() << "SYNCHRONIZED");
}
}

@ -132,7 +132,7 @@ void StratumServer::on_block(const BlockTemplate& block)
// Get first 8 bytes of the Merkle root hash from each blob
for (size_t i = 0; i < num_connections; ++i) {
blob_hashes.emplace_back(*reinterpret_cast<const uint64_t*>(data + i * size + 43));
blob_hashes.emplace_back(read_unaligned(reinterpret_cast<const uint64_t*>(data + i * size + 43)));
}
// Find duplicates
@ -251,6 +251,10 @@ bool StratumServer::on_login(StratumClient* client, uint32_t id, const char* log
LOGINFO(5, "client " << log::Gray() << static_cast<char*>(client->m_addrString) << " set custom difficulty " << client->m_customDiff);
target = std::max(target, client->m_customDiff.target());
}
else if (m_autoDiff) {
// Limit autodiff to 4000000 for maximum compatibility
target = std::max(target, TARGET_4_BYTES_LIMIT);
}
if (get_custom_user(login, client->m_customUser)) {
const char* s = client->m_customUser;
@ -370,7 +374,6 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
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");
m_pool->submit_block_async(template_id, nonce, extra_nonce);
block.update_tx_keys();
}
SubmittedShare* share;
@ -384,7 +387,8 @@ bool StratumServer::on_submit(StratumClient* client, uint32_t id, const char* jo
}
if (target >= TARGET_4_BYTES_LIMIT) {
target = (target >> 32) << 32;
// "Low diff share" fix: adjust target to the same value as XMRig would use
target = std::numeric_limits<uint64_t>::max() / (std::numeric_limits<uint32_t>::max() / (target >> 32));
}
share->m_req.data = share;
@ -686,6 +690,9 @@ void StratumServer::on_blobs_ready()
target = std::max(target, client->m_customDiff.target());
}
else if (m_autoDiff) {
// Limit autodiff to 4000000 for maximum compatibility
target = std::max(target, TARGET_4_BYTES_LIMIT);
if (client->m_autoDiff.lo) {
const uint32_t k = client->m_autoDiffIndex;
const uint16_t elapsed_time = static_cast<uint16_t>(cur_time) - client->m_autoDiffData[(k - 1) % StratumClient::AUTO_DIFF_SIZE].m_timestamp;

@ -32,7 +32,7 @@ namespace p2pool {
#define STR2(X) STR(X)
#define STR(X) #X
const char* VERSION = "v2.1 (built"
const char* VERSION = "v2.2 (built"
#if defined(__clang__)
" with clang/" __clang_version__
#elif defined(__GNUC__)

@ -136,7 +136,7 @@ const uint8_t* readVarint(const uint8_t* data, const uint8_t* data_end, T& b)
template<typename T>
FORCEINLINE T read_unaligned(const T* p)
{
static_assert(std::is_integral<T>::value, "T must be an integer type");
static_assert(std::is_trivially_copyable<T>::value, "T must be a trivially copyable type");
T result;
memcpy(&result, p, sizeof(T));

@ -76,6 +76,16 @@ TEST(crypto, derivation)
ASSERT_EQ(buf, result_str);
}
else if (name == "get_tx_keys") {
hash wallet_spend_key, monero_block_id, pub_check, sec_check;
f >> wallet_spend_key >> monero_block_id >> pub_check >> sec_check;
hash pub, sec;
p2pool::get_tx_keys(pub, sec, wallet_spend_key, monero_block_id);
ASSERT_EQ(pub, pub_check);
ASSERT_EQ(sec, sec_check);
}
} while (!f.eof());
}

@ -612,3 +612,103 @@ derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 15 00
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 127 a6
derive_view_tag 8edfabada2b24ef4d8d915826c9ff0245910e4b835b59c2cf8ed8fc991b2e1e8 128 0d
get_tx_keys 7d213608e7a34afc5a49213d3062f2b9897d8d0b72cce11d308b22fe8879cc00 45f901c7f276eef859f7311794eb72f6f4e960a76e14112ee2dd70e2024f0b8d 77eceaf79ada2418adaa763b5441734140a2f34063abd58067ab83d524de421b 1e7943cffd43aae22631bf761c64a14024a000119cc0c53462b129865b9f8804
get_tx_keys cb7414d997b129aea90b51c0708a7f0aa9b1daacecf2478316fabff216518090 7dc8479adb9d3bf5fda5a375da1acc970600f28c485dd3b7b9dda56f3a0437e8 4663c0bdb16a87874262006d7645abb6dba7f29cb3bfd56b220a66185b04bde5 a87056a6fa70a6f61a6494e7cb6eb952e7e4f66e6745edd80a30204d877ebd0a
get_tx_keys ad2d1d5a2d5f8cffe8a916d755cb05bb7a12075084f3fcf7382809796aa324b8 d9baffe842bda66d197de65de34bb33345d3019d67624429c9243aee9101b4c0 282dfc1b0ec4c8e7bd26f842316d9046ab73ea4b7b24a25e15b4897d538af9c8 943d89bdf4656d6133062c18cf00a427c809dd2ab7afcec7587895c3dd53e80e
get_tx_keys 5fbb50f791424fdfe170f738f776fd835e4337c8bc601f8a94057473da729727 232729b54eb449cb64e2fc48b63a85beffc8b045e9ef02a5a3d5c48a85a9e389 f647c77b3104dfb9f640c5291a9fcbbfa2eda03fd40e5c3ffdd85195540cfc4a eb664101ea49ec81f14a94a0fb2d4614e8a69a7838da5c304781452ae314c80d
get_tx_keys b6cccd4be208f52ce97bfbc466ab36a85b0b079a145c8088ef7659d1a7392bac a175c9e8049433a6f075efab7b8882398335cf53830721cc1c300f9209e2953b 8086df212aaf26f80d4eb2b9330eab1827a32dfe9db353736cbf01ad4fdb25e4 15940148e7b7ed1dd7917503be2d315509b7b25e6c7df9e67b1033e1017e5005
get_tx_keys f888ec23a4509080ff7566e664d573068ba418bf2a4be5c1b3e3f058eddab541 3a9fb72955e4387cb0a6900a8332aff577fee7cdf40c82ece12eba1275877e04 0a7a7f8fd15e77ce0d2fbc54e2bb8f4772d83f88144b35a151481f79345a7d23 2ca319bb141cdf1cb63965386d0aa048bf414abc8459674bd5d6ccf068cc7305
get_tx_keys 680978cd6a075eafe256f324803f93162eb45d1b661684fd186c7bb03fdb24ce cacd708ae9f2128af088be8c67505e6fc779397850edece6cf32a855be488424 b27bd9a14ad888e213ea49f73304e9211c8d5a84413c8ebc7705ce9b0469ee46 3b10110b1943811ee6007fbf4ada868b77897fa38628ba8c5734e32f764f760c
get_tx_keys b205d5898f0cf7e07da7ab3135356949d98a0e5c8b5b3b5e256a4a18a29210a9 e0aea2ee0cc82bc335f3ff64e002498fa481d9d55253ee87960e674a24436161 ecd1e69fbb7fac82f0ffb678a88a52edf3818275c1f234957e2b9db5caa1730c 7c39af98867a8f930af915657b5aba80fceb554f3b4ca8734a216e03a3f7b60f
get_tx_keys dc260d51959c71cb8ae2e58225fa0e1d3c7628b25f109ada83dbd2f4a127ece1 ddbeb64ec8ddeca7793307075d98af9868d618d99076ea3139d5aa5eaf7cceb4 f18e457e373e93c531cefadd4a6621385e1afe674b1534a6e94c13a35752d30f 485fa8917be8ef3c329613738cf83fc3bc3518228166405020c6116dd0dfa20c
get_tx_keys 29c31aa128314e7c7156892d915f1c4ac432f756e0672552659759ded84caeda 274561d25365a1818002f4dcd70d44f3020affd45e7f6c7224caa4849fffffd8 749c54df47e19f8334db6d8c733692f8c58bf9e8627d4704b465d0555b6ba3f4 1b48bc7f6406941ee97152338d23dbd05c31b4921588e1929865c05a3d300f00
get_tx_keys 965713c69ef2baeabf7f1fbd358342040f3209fc30ac6d453a09a26e05b7ff21 f3c23092f3e1696427b6d0ae4fb444514d77562cb7f791c69ab723a74f9f47bd ba644a27c2751318086ad68970163d5c40807e720e66bfc1de74e4008e4bae20 6848319083aed79f0700d289f2a34ca2fbb36f843e90614caba1cd80ee58ac00
get_tx_keys 1cfa4a251bc47d7e74bd6c4dd95bf9b58303735ff210aa4e270f21c33c5ef3f8 391673e4676a6fc831762937419aa87451e60ac22c22921d8321a87d6f64c229 4aa5964c3c1b57c4987681c280d156865cac8957d83f0e56aa38df0fd2a37d9a ab7b8335d57ec1a296b8861658267ee92ad896bb188650365fab8a093d95570d
get_tx_keys ef629fbf9073a4cfa76f145db5d43c99d3ca90584c51fdd6f1eb6be5a4458909 cd5b5d849698e6721f5878d92c168b857f31667c86e9cc73fd242c22d0d6bf24 367d3a1e94e5858fb6d59bcda7fe3db1c10f4792e6c2091286599878b2492ed9 54f4ab64cbf2480247337c065b6efc92317df41ca62ca5e720e0cc4532e81b00
get_tx_keys c0d24de5d480115d4c47ad95a70dc3f0def037f0fdf6b503b7b3991930919128 9963e81c925425d67057061fe9d42d99b871b34c3b3cc5d4786a8a824b2e3b8c dc1b9a81f61a6270576b983f7d9e0230d12bd79935f23c686bb65936bccc6d85 a66e2b899b74ea804001dbe84939c584b939a333f116bd7d37ef2a8b5eb16d0f
get_tx_keys 08bfa388334c3d52175af77e1dba5826314e92a6a1598c2fcf152f2f28126f00 29ecd67f1cd2de89e8ddac937984b8b84f1413dd14e960f403881168a9256b59 fd36397c3c681a7234eb633f4d998507aa04812b3ebea211b93fdae4c527490f af3eac01f6b89830f5fd624b3205996b3bab814c7eb28e431c75be097e47d404
get_tx_keys 3e10a312ea2af6e0d23ca56eab5a62556f165002898d0993af852355434f93f3 018a4a270d0201ac517e45c44cfc10c1ffdb6b81e67232bd92adb8683a99817d c5a440bea7b83325fd0c9c2150cf43f6fd338366262c51ee8a0a4376457467e9 bb15f85e1dc7787a7e8ac45a2fe4c3728c06ef8f6372db9afa13d8ad9f39ce0a
get_tx_keys 777a5cae8de9cdf45476b4be97592110043140c852af767127ac06423bfe014e 364a78440eaa43ed26a85ce739afefd23695f6b45d1adbabcc672e2b07fe388a ca1d0e18d8bf9c03593dab4e1e6623bd090f92f62058071ab6756819be5d691c fc92965ca30925459d365e24e0c7fb05caab71aaeaeada9184084e994f698004
get_tx_keys a2b147b42bdf67eff19de7b2ddf113bd529ebd3bfa3c845df286181ccd6f23e6 73b7474bda2200e19b0ab3225a45d28f3aaba2ad0dbbecc9794197c1950a6bf6 981bd41ccbc21a24c34929ee24b5827cad77cd01c69111ce21b234560354163c 8d6718efeb60489a808ac634a911e0059af8e6196e1b26a6109130a46c010302
get_tx_keys f0cbb06561a873ee4301013c40c0c88797ab3f5f947c812a22f3484a5f3368f0 3870ad939b1b45de04b405584b96d59da4c2a4006fa3883fa5836c8c425b64b2 5449ce32f1a467bb2185adaa8a10f65da7cafd59677346dd43d43c278d1f60a7 9c75e4dce83a72d37035d75158a4ae722a6bee5c0ead55fe14bf03921f4a3c06
get_tx_keys ec5a0318458911f5fc86b04863a6d5a7b54d38ccf3e652e5efe3c33ffa5702f4 511f4e45e347683c18287b614d54971e3db403acb341934cad471dbc67ab8807 3788bc7513f1614229142de2d733495874040fee9e62900b6cba56129eec3388 941dafa1009fd39c8b4eb467d2233594a056f24697228dcbfc3785899af84701
get_tx_keys 68d0ad72058a0c1e5ba69646e23b581db24a22cb9facbe073ed86bd37fc7c359 89c8bd261c80ebb19895a8e18b9ed185785896eccce16db63ca52d89b158d198 60e727705e8f18456d0fd85a2f17abe9ccde2279d3db52d31a3c8a8a231035aa 1860ced53351185a3bbe02c85c5f43bbb274981acfc69f6f6bba936b59d72e0a
get_tx_keys 21ee6d21afa277942f4c928dd50eec19c221522af4c1d1e106b7166547029a22 6a755b46d23a9e9e87f93c57f7ee1e871c8350b4ca0bc5b68fe4d656d28fe3b7 de3ad7466700f2493594890f5f308554f604840ec1761296cff926d91ac2f9c4 b543138115bb436b4b80293104baf4cd26ab076002980a1ec81f0916fc94fd03
get_tx_keys c9d3d90e061950a95a465cb8e7af536d988bf8e309a9ffc9825f18e49f0a9196 bc4d4d09464f1100060c067dc8db960559ec71ca1eacd609debf1096536981c1 7840bc035a3368795433b8f12ffde37e5141c66286cbbd92a4dc792d9b33fc3d ee4b1e537174092b7921d4b580d2e9ef1a3915c418680eb1c0eb02eb819cdf08
get_tx_keys 4e58dd10f17e0641a442b60906c2278db341268ac8b3b5b21aa8b025c0987a6e 6a79ed1294720d312ea5e52f1bedf9f5c98631340abf779c922f5c28135fed4c 0aba86c3028654bff6a0592cbc2cc1ad797d7b050063b16c03aadfa446029e04 3a17775b8771abb70bab0884423ef8000189923465ba7886670bedf8f0f23702
get_tx_keys 8875e0af11aa579262dffa6f97005344fae1f8291820b05f983998a1dbb1bcce 7a5f37f62c730bc70d60dad756ed6c0704174e72774e4a141bc744f7910cf4f9 e308eb4ba46583ab9cf5e753f6c36b64b871da7ad7b48bf669f17f615332ec12 30e7ec75f993e770ffde3d2c9751f45d7ec9d9535a7f7bf74c2eb1a3ec573a04
get_tx_keys dd9ede5ce885341ddab187bbeb38c3060d5aaa765d0fe4aafc709c98187abed4 2dac082c386c608bd6cc2c87d7d6058d13ef1fd7a9b897cde43ebc59734e216e 2a7eb8aafc247abebdd607cb9fb6af176c5a7ad86e0a4d7772bfb3ddaa943f84 53e661d255b4df07a11495ea31bf9f1a27dd54d5f7d21247f6ff80451fab9a04
get_tx_keys abfcc9c0db1bd5d50e8bb6283b962c10f1d7b25e60a199d21581fac7701a3a0c cc16f91935b63134799cd9b1c3c56caf8119067e7ec167ac08302ba0e944b3a9 13cf3d6f32c5477023d4efacf5606a91671e2a2949014fc038c6b267a4836e9b 20728833919578bcd0decfb0ddc4f4c4c99fa4a7f1e764d2391bf29ab4971407
get_tx_keys 8fd65d251c53639be6f754ded1f0ec665ee39de2057a0bb184410b8aa5500cb5 902bd4c455f09713d2a9d8e3ddb35707250003e144cf5623dfda1987e83c0b14 7541d3100d8050094cf919341004a8dcd05fb3c0de0d3b063b216965461d98a6 8072d03d327f2aa94bd399aee45a342488161ee9f4e0e6476b0ba7ed0b20e702
get_tx_keys f1fa57291ceef8679990b9aa3e9c3d15b2e0ada9032d067c792294680b7167b0 51f271c28f0e9bcfe9979a765fbcc43ccbf1187d6336b36fb2946b7506ce5101 f8d86820511f6ca96e36c2024315b2785c0db0ee32a8369730a2c34930a6da6f 56801dfe40886f1a423eeb0b473875a6f66535202bcb6e2cc3ff7966307a4a09
get_tx_keys 7c90e526fb6b0031c642b0eeb409378d9695cc5ef528c5630e097efd17beb8a6 66e35a7f40ff2fa0e0ddb1cfb16eac815bbcfb3b6e76ab442ed77a5a35df68f5 c4d748900af239a2bfdb93a8e0a1f15fd9e8dcbae1ab10564accfdfae464eee7 c581bd235ed3c4f5bf2d363bf41094cde7fdb6b62f7982e180687092f4224b08
get_tx_keys 7eadb928f739e7f9b22082a5bb1233da66705b5085808d8d9ebc0803aadcd6b6 3a2700ff1a5d4ba6585e6f5eae5ecd7f9547580b7021d5171a93dbb80adeaf03 38614ac9e93f77877dfd1a445a4e730406f16ac2a0775e341322492b79f84d08 23a53a6327d989b5cb359eeb65639eb6bf6ee15170f6a0b690fbc570a919d90c
get_tx_keys 5d8e8ab97b66670a27e4ee92e5126bb2e8a2965ff412000afe07296c82d48669 f55b5d99a8f57ea4c0028c980246b33e084f38591bf285dd5edfa10ad3f3b2ff d6b3c98deb8469df40558654dfe60d0a1aa48caf8778d5c72c3fd6fdbdbc6113 613291ee208e2ff3f9e9da8ba7328a47d2bf97c6850c8341ecdac5cc750f0107
get_tx_keys c715496080b7e1b1c7289060c9e7885460f8c646f28bb65a6d68b87bd49a9654 c73f7c0ed47be63c79efbef9aabe737be7386ae374b55dd7308c4868f3f670b0 0d3fa599cc8771fd7d534c668d455eab08c3a64dfeb33081ca3e536faf7d2057 82ef1269612172de67b547769190a6da03855cbf8275ed5bff3bf905e76aab09
get_tx_keys 5c2a8943a2e01dfc18c78b0ec92d08c812e5bb7725973237876297d2282231d4 cac082ac5164c699a13dc1c2c093577679efcfb245d565edc799bedd674a54b2 e441a4039f9e5242863e5fe11e17a471567ae0f9244a38e6707fc1bd35b98e1a ed156061248e5689b5b0219c277d3f6bc8fc014a7dc68449e74c35ae57580c0d
get_tx_keys d95a3de19720a2efbab2b3d3ce9968ba4911d7e00c0a4e76d670b11cf83f8728 0d7bfaf69378a58892eaf0ccc635e1bf4f072563ed2ffea148781b2f7529cb1f 8a8a1be409942bc52727c4301e3d051c525a5cd36c4d7f9045c8a22ad4b4dae8 e47143db7fd07db864ff070f7881265502c794a53d60e0679ead49a0e49f6f03
get_tx_keys 70ff3e6d59e894c22c0a93fd9255a609c21085c3ffd9ce1bcba35b3d2dac759e 05ec7e6f94c6a54b17fc1483475db0f4e91ad335e85724dee94a0b4903fcac17 136c92c9d348eea13432172c93147f540244c2daf3a4e4463863abaacfe77704 6ffff5f891f49198eb1538fb957ffbb2a96be2968b0c7fd2c8a0bdec5e48ff0e
get_tx_keys a839b8447a80b8a6fdc772403f934693163a06abeaf59757602599cea52d0d71 4836e0593eecfdb95b8d8fce89c270c6eeebbcd96f168d1071f6777e3dc5b349 1fe10336a5c1e935a229e990eaef309e1e67e4e461710397cea9291389b06fa4 915b97bc764e1d8221a7f611663af586d1a54d3245ba1f938985924e91eb6902
get_tx_keys 29642b410315f45037eb07bdc72b1478b1f4cae7f812afbce082cce0f4191307 83133d6c4f6303ce1a33a6ea80c37d0759b270459eb2417b17fbd53df8fb1863 d9068c3f9072cc7f5bf36f0121309ffb159b087ef23411bf7d8c738d535bf9c1 7d1eaf8883dd981a0e135d4e01caf6f8be27197e4305339198c02ce8ca31950d
get_tx_keys c7296636717cc95d9eba396f76f21607dfb4d6e68e2dd267a9760ea890911f4c d4df7df518ae8a96e1dabd60cd704fed4309e9b33084334f7e0a7bcd6fffcf88 1609b685395137b091c8bd1da6efdaacca1590cd1221b36a808fe6815c7049f8 be6209f2b825308d9226766030facb220ac3831e6025642c5f74194d091a0b02
get_tx_keys d1437bcf292416ebb37958006fcaa2e2185dd733959b838289311903c4a6ae0a bb4c3c84f8333142cb343e932ad473a54ee36fdaa2167f9319d29f06930f88db 9ae92fab10e1dc17561c6f1d078eb49d3c426cd59ca7db1188522f9802cc44d7 5229b9103403ccaee5ab619d08217c01841b6212140d5aff8704d55b38e10c05
get_tx_keys 670b89234884058655da41478dc455bf9733873734af3961e3fb9ce3643a29af 3b3b77f60ef257d9af6b368d99f1373dd4584536e6aa78f95e4065e59c43e1ec 1ada3623f6f276a22ebcaaa19f3cba331ff926b6c7a0d2e1a0dacc0c2d5b9dce 1b9aca32ecbc901adebd868773cafa597c01b911349f37e55ef25f2989faf803
get_tx_keys ae2e56b11b88982cbf8cf53b06ba2e93dcf7eff5fb90a3d9fec270235683b52f bd79423358bf1456726f3d4b52392feab4bb9dc52c6b768e5878f9978978b362 90779129bcf492565880299f43512f9a1c4666cad8050854b947ba782389bdfe d682571e48c8ae2354041d96bf954921f242c778378f5f8de514c8c219bdc103
get_tx_keys 1482f4d9bfc7fd76e94a7a7a9bf490c5321c523c816dc1d5661d3919da46ed0e e1d4c9b65d0d102c748e638c4008c02acd5f7bcfe765d9fcd0eaa0f9a74957bd 7041007cf6a81ce6700adde666f96b0fa1043121f206db5ae15f9c68356d395d fb401e12bd69128644145bcc3d68ceecd0d8fe89c208f34e893bc99f16e66900
get_tx_keys 3b89bcf13340eb1e7a95a39247074dfb65fe9902c30284180479d678985ed9f0 1d1b3343461a2a37aaf5c21ca46f3c113cc00dea355514e9b4e96a299fbea830 32ebfb5d5f1ca38e932d141f47554ee55f6ca97ba0af8109054a7286648ac5c1 0f0d337bdd6e62f6c6391c12385017bceadb64bf6dc46b541ca2ef1cf5feac0d
get_tx_keys fd9819750073cf4187e941dacbcfabc68c524d0fa0207f3a368de738637d0d3b 5cd4351c4f379138967faa094e264589c771088bac027fe21b9af99af9db420b 56c5976a658f381152040a692fdeb7c6b0f72152006e97646783ba724573d8c3 9c9a2074c66f360ca81242914024c7d5095e293478ce4082b176aaa76a36490e
get_tx_keys 71975b284f84358d08a50e436f439d2fe0e2dd00b673121d172bf21b0024f711 403c2a4d011af5247a36d22d116ff30206ebb91538636ba3fec81ffc4cf4adbe e77eb03578e1e3431f770af237544d17c372bef8224de75cd3309aa43a5b4269 1933c81fd680e4a269edf9626f7407ef2fa7be30432a7e1d5fe614aa257f4808
get_tx_keys ba6b0b1456eb44421c9a7bb0078354b289e31ba2a5e076dfce8317c728916049 0c5e1ea6ef614c88e1fd24d42b5aa80155eca5d54780c17f9fbeb73bea6f3068 b4d9530a797b18284b39edc7733cbf8b209e63b9836977ca6213ed4e5b2da7ad 88d8c4bd9403085880d12005640ffcc5aceff219283372f11fd024624123d905
get_tx_keys b785b09e3f5c59e11674b87deaded5a7c391c8c9bd0ba165091c9d187650777d 6f7a6b0125479ab865a4181a8db917e605abb5cd9847eb2f02a8355cb13ddb03 1f36978d1d57aa644a0c258028b99e0b44708b60693840aa6e50ea554e4b0c27 5c85671c2e292fa60c4f9cfa8f96da27f59067fd81f673589de4c24420e62d00
get_tx_keys c9eaf2162541ff394e8ddcc22ba1cdedbfdf22f3253c97ba52d6d1e303f32f43 1c4533925a74b74802359da6e2b04fb9595c2768ad7a3ec0160c447a14d9e559 66afb680172f358ed91952a41e99f36db723489bd5b1d78da0f0a234859b37ec db9e5c807116f93c807f78e00e7ced552327845d10ded1ea1e4a72846f65790c
get_tx_keys b7afb73308b29725ab8f1570c66def24768a905a876972c1bb46cf2b4c708e30 cf6e15c3b116b77c2fee274a7077d1a06c5c950993b8e658fcee2c81445666f3 d7ca8109d3ab3fa2565d2c0213fe492adb479aa7bf1fc9d27e83690871107e0f 75f140924ce98b4be06488a6332bea456b6850356854f03277d837981b9c150b
get_tx_keys 3894d2c95e6ef6e822d256ddb36287f82686cc048a1bcbbad5be009f28384e4b 8dbc226625d37c890ca770023299f8e142e0e6df9ec530e971db11d6a671f41e 2c2e705a66f983e87760d55aa6dd7180cab8864d5e412a434c1646d33d164e00 7d1777ec04e4b8c29037c96d2079bde9ab4d167510fa8875b95a2764a481470c
get_tx_keys 65fc7c48e0f854d23ec5cb7ffb818ca0149c9f53318fc5d22a96cda6ba971801 f5ce97bc593c4722cc47cec070c13443441905e269ce347f6d36a3d508859f7a fa077f7b050c498e0c7acf57fbed1f7a0f2151ab33e4d748bde6cccea689ca76 37670bdd9f503f2c25be9315af20baaf5d58b336cd1a6e684f6a62334836390d
get_tx_keys e3cc8ac1113616356388cec01c65a9f44b9c44ab294d5f61920c98bfd2406e5f c4d8db10884c8c4e2ac0adf96c89ea7cae962baeff4219fdaa99bd345114907a 5ecfa6faed8615fcbc834b0be4e576e763a19eba4b7ff9613bc46a1390136fb6 aa1e0e91e146428d01e27653b9d5068534eb914feb52d451cbbc7774e74faa07
get_tx_keys f89e4e5a305a4a5b4aa9dde28c363a7ba45fe76ff13a349b70b802db46fcd013 c3f02cec4e329d7783eab3b11e335e7c5623d2fe6e7dc17e6b547e34913ae311 597088b23f9fb50b35dbcf3da00398514e7c49c9e170d6b0ec98b8def6ad788f 13ef669bd2b6b806372a6395c5f44a77d9a358ca7ef803ac79448bc0c436c802
get_tx_keys 25f50275374dc7dfa8fa7849cc9137bf0d11c34814f1fdeba4873fcec7a5230e 3455b25656f286fea6d7574149f51696f37aa21ad216c8ee99dbc73296dda4a1 de599dc29774168c28d548ef1fd34590b696e025ccb2e794decf3e760dda2f70 80d9417d7040df698baf2b1192047fe71509f740ca07c101f427aed21b9d2806
get_tx_keys 4e45dd055bc760d0db030f1c7e28e614f3e6a021a200e94aeed7beaa1003f531 821b17a52b4a1d03078b877c591d7eacdd176626558fb2715913cfd7003b650e 8d4eb74b299d0f566e1affd47ccba7b9582d824e25392d63691d6ced55e1f0fd 21869058ccbf6b60a1cfe691f431d4d1338b24e4af971986dc7d0e2c5e05a00e
get_tx_keys 23f3d5dfa97fd1d2a17e98825b1f9beca9458252e576ac38a54403337a3e5f38 d15357b83d4b5b9d4d0d59296e97790947385faf929b5ca1d001abf636e6d0a1 73c529b07adc0d3b3b62304c38a67a50d195f52172b15fb7f8e4d7632f1db32c bf527a36d8ac5c237884082349b63e551294d0eeef1e2dafc57141ace5bf7d04
get_tx_keys 5734265bca418d627f6db3079a958f197b6e87c94ab48acd65e95bfd8daf1399 fd84921d0e62026d907db32ee91625bbda6cab759cd691e6af79b5bfb4c37ac3 c27080d500da75462d0a4450fc2e635d0f07077eaef3f259f5690f5810c18318 9884c837cfc6e4410e389f4180afc983d97998b375d5ea8701371c53d7b92c00
get_tx_keys 59f381ac75dfc4fc31e66973657db0b1fd13a247bca04ca43fad62e504d641f8 1c7403b3fb7b27453fa1c980310f82f6e090bb57509ff79069183bacdd4cffa8 5746c9a1acf5e234c1fc0c83d8694267f90079a585a4b96557df8544e5f71eec e6cc4dcb5da8afa62f8d2a222332d574b48d7fd0049abe99406b5fd3fffe1702
get_tx_keys d3dacd43269424790e0213444585de4ba7930da1e2edcfe51f7affe24ec8fe90 ab5b495738590b75d97b383c7b8a4dbb6f8e8bee797bb92d58fb54e3434ccdd3 ea14af48cdf17863f051cdcc79dc96010573ee0d06183e1741ffe6bd45843a4a cff182102a5477352e09b4359e446546e206972d458ef0ee7f90d0c85e2e6208
get_tx_keys e8367236c6f9f331cb5f0ec3128d68af1d3579e810dfd0e4b8eac5fada9bb17f c3415a5ed2cdfe0ab7af2966ada2aebf165536a230e478a71c9afa26998f3b71 db01145a152d4d567e6177a1c8e36d7ef0a0d2415d5d43dadea2260da1782b46 82a4206199a9bac70e055ade8217e481041da50c0bf61ae4d54104087e2ff10c
get_tx_keys 898bacc17b2e3bd33d27b31554a7b0479ffe2cff636a6167ab191b13959ead9c f19083c30751b659e78fbf1cb4b1d9bcd8d9d18dffb763cffa8b919d97274306 1efa8f3cda03a6d415755c695ffe1790195b0b9bb98de1f88f1085d731399da8 b165a1e578f4a44d7abb9727114b440493600a521afb501f6d6fb3f2cdba580b
get_tx_keys ce3080c09c3cb0902afb640debc717d7924793586ab88e5de59685f5da682492 d887f1ba8c2e90be9fba524fed6907a5906955426d127b513a6fbf3851eafb1c 61d8fa7c7106d2d4bff4976c30aac5a7a2057125ffed6b78f9ef6b46bcf0bf24 3d0a5c6eeefc6c37a1a326c96e502240b47d01cfd9e50d40766cd51965a23806
get_tx_keys abb68d3fcb6ec538adb3358729f927b607909a284260d2eba98b0e6d14e33aaa 7c49eb9c4eee7f97a2a29e2feb77d884aacc8b04e49a49438d8fe79381203279 b4f0ac34065710dbb427ac06a70c87e3d91af2f12f85468d44d223060c9400ff a97c732baa1c831831941234a41223cba02265f4b19836cb7cad37d8a52b440d
get_tx_keys 9754ccf2edc3b31223f55cd8bc6b265f5a5ba0a858f17557e0ce0f20deb87ff1 b1a0fd22245b7d482b8fa994478ec995e3686af3eb1a9e7ecf24d18459f4fbcc 5196cccb6d5a3c018d0c69d5f30e392c44d85b282aba67dfea04c1189e469ff9 119541e6d6fa0a48595a59bc919422b93ada2b74626dcc80108d652216755f08
get_tx_keys a8fa3f5c1a5045f838d05ce5d9dbc77f22fa22375aa8f3e42067ae035ae23fcb e89abd4ebb306f0477945a39f27269c552d26331b9a811725ba97099a40bf260 2b11ae92b8619371dd998931bee58e21dc5515d0f4023a6ccf353708e08fea3c 8174ab01471a7f66167bdce59bddde3569f14b4a68103b82ae937cb36912180d
get_tx_keys c69a78b58da2d76a252f95adbb48ac6dd6f4b25fd079b5023948415d7635671b f50bb00df3b5b2146433bc6f9ff871bdcecb74e66b5f49a7423bd2536d332fa2 12280bc919532e508c8359a76f991127bb409e07472def74ebee79ad81e1e92c e509c3993ffbeef2048fa64d9e9284992fdae576d37307f40f4d29aebdbeb407
get_tx_keys a595f6249259bf1ea8edd81ceba69d7b83cf0d40a5333e3eaefe65df59872343 ef870e14f0eaf6d28e66f380c1cac12c2e199c84d97b8faa350d1fba01519e70 71bbf15207539db07915189db1dc8e9afbc7bcdfc2ae61a7e0d1667d0e2f2a16 e96913a55b39bef7ab2539d9cba632302a99337c5bac67e63de0015110fa3d0c
get_tx_keys 50587d7ab611785dab2d68f51c83450d4b1a1eae2591789e86c30ea9787469ce 3e58cfc6c80e559595212fa29cc9425232ad744619f39742c166ba3110b4b422 b4d1f71056bd36f7ce41a3cf38ad62e48c847114afbdf1b59c0d160889912ba7 716828af952c03c5adc603914eaf28ba49fc8be732588a83914870262cf93407
get_tx_keys 90797312faa9dcf29fa50923fa98958712899d17839c52134040a42705370fe4 89b7e6b64425bbf46b93c6eeb8b5d739e2f90e9ed606ff0b2fdf75494e4c0522 a24ed39281d69e2e7892fdf90ee4448d1618e685a0cd0a55edf6d4eba7af0443 f5b3ad9fc78524aeeb4f8e8d41b1bd179edc63474c0137cf257fd5943e10c70b
get_tx_keys 8477be33825b347ef6e48b72bc8e68bda43b7a1d059a3f88dde654516e3094ad 5817c074da79dd5ad6f673adb0290b843e931813f6c52ebbe86aafc48a608779 0793ba243afcb6c6552861ea101f5ed9def4f0e085018ca0a756324c96d58f50 a514b4fdebb7b446774f34e756b1ec7d2260a3dfce5a4a908c76a61db2101209
get_tx_keys 2dc49ec635d275e581b679b2b6af0afcd8c6a472e3eca8b78013b908a8475786 0b43952461bd4e7f7941d84bf19e2d2f6b040478128061feb362d5875bd77772 adcd427735fe3da32446a0daa57351bd498e9d49bad3f752d7b05725899ee2a6 5feaa761854e1965d15d04483ff16243e134a8d5f991c9043836d17bf1eee70e
get_tx_keys 5ef78e154a75373babb1c61908ee1494e1116076204e6522cd0e2e098e75f9e0 e1a59e607e5ca134e5914b10ed9c61f8ad18acfe4baf65ac8ad6579ec4e22c8c 64acd6e33c98a81dcae5fb78463dc8eb5d6e7b2e4c6805cc15a15da047984c03 3741d642d87db08141b75f00cacc45068bd7bb4ff000823dbf1561ed9a77540c
get_tx_keys a722ce6ca6a1867f050b6d9b8daabed06a9e7cc63922d273d6d164b0b75430c9 e72d0f57bcff9728e0aed501e11620459b8e6cd10ac7f9ee8c67745aee01e466 1dea63b3fd781f20feedb428bc83b4b36414bb7e21911fa698b09bc4dc08af15 4c61cbf3c88318a5831f460fa7141519a58f6f0d4ba57125ce0e9777b29d2c0a
get_tx_keys 521dbf95251fb653465457d9a90e5b51fd2dbf9cc1130806cb19b8ff7ba77700 5a6a753cb91e391a3ceb80842aa940051888c3c0eec8fa4ad7026001d0839b20 a2308ad45e85c2d66e8ff771fb7dbaa9e7113dc4989ab1aa0f8ad52da09d3b5e 99205785b42e2756df10c2191ad2ef93fd02fb387ed28b4cd9afe802f74ca509
get_tx_keys ddd91ecaa02fbca1583897d425b3ab2afb0165dabc7817a67dfc79e191a092c5 d8171e9ecd6990ccab7b5159f2ff2c817ed1d2e869badffc241d53064686ce70 8e8ba878576f8d95af4c5e51525b8fd6114c334d57a9133a8f0fa44344c4d21d 789f91f6f6709f546a04d87cfbf04104f4b36ac29b961f9bea07f57d9ff70304
get_tx_keys 636fa68f249f6b516b08539b8adeac1f7c98eb9080176727462315f3c308c24d ceac78f453a090d243cf4f7db75e148c4f47ca832fb358f80831c2baf574e391 a24ed0a2ad541f93f3efcab27172dd4de5c6f2cf227aa4f8aade0c89ca7614e9 5b8f7d3b7de82dec696d790ab7ab0ed6b278a7b8b4a7529b123c8f4de5ff1d06
get_tx_keys 93124f6e215d53db70823ef7456486aca9c230c9de82305364ae046e67cd413d ed1bdf3b24a2554edfe92112888602680e4d73f09577aa5c142c2cef9f030b18 206268d2812503006f3a8a17c8cad53c8f3b3403a4a327affd59d67e443c73d8 3ed5f8fb02398f365e3c8e7c5d97876e7121069260d31816af0b06fda3759b09
get_tx_keys 4e391a4f385fc55fd02584135bffe6b65802f57756cdbdd521b1b6fe551b2781 be5d5bebfbfdb4faf81e8e54636292dbf3f257eaafb1eff6ac085775c8008e89 e8eeabeea2c93472122ed6b9c04a16a4a2ee296f9cdfec05010041cd810a38e9 7bec7084624e1e0f08470a8229831762bfb2e8dedc5e13f44b192b52dd5d1600
get_tx_keys c4c91b14b9218ccf7c463df46c9300e4883b2c5c3446eb13cb23218ea298b64f f92777fe3578b617038b8f1411558af93563252f6a33d32c7d07806aa1315775 35bc3ea7413faf8f8a9fdf3c022d142a4ee706c299d2a5b79800bfb6e57f347f 7893715f9b61fdba063cb5b63c651a2ce597173134df28eda28cb2577fd08308
get_tx_keys ef6d5edc2354a6c0d83078b80e6a2e88eb6796fd556a199d0182cc510b4f07c0 0f8b9e35dd397f3086e7e33949e9655a29a20035247e1b528ca450328459aacf 3d457bb0fd233b37cf4d994860f0d845c28823bc4befa1e04a023d6b4db6eee8 93432232a41f521477768e37367b1e4dbaad8c227ed44fd6cfdf9e42d3c62107
get_tx_keys 5b76ef46dade8c0fb949314f0c918b24476bca2973535de9a7e928cc65ee47e7 feb48276d4b0fbfecdc0aeac8aaca7837fa17b25b036c0078e689e5fe00dba7f 49de7826886b329a961d047e601077be5f56ee5ab53f23e96d02b22e665220fe 37d5a15556ec979b95a56c659d42a4dcf58af2c8ebd40d5a3db14e44c5074f0e
get_tx_keys e99281f2e145f20afe1869416bd084a0c501a8d293160da8a42aba8734e8136f 3d2c5408c26deb78bde28e334efe1aaae2cd4b76a32dcd391a7e51d6dd515902 ebd6e20082bb206de25ac960de53afd1a1a43b310a7e6b17737030042b4b73e6 101ff10911e84a5732d06bd6fd4c1e14deb534ae1d27d5533df27e0c931c5c0f
get_tx_keys 52185f668c23a8f6b41e7255f1bbfd6372c9a9d251daf710af4eeda4c489fcce cb9f3d8e4734a4fd55f3b78581b9c22e7235087522a590f09ce29fb4621fa360 a3f62830ffa89b6529260723d95d961e12ba7840839b7275766e4b113583b784 c05c085109056b3561255457e42ccab3499b1c219986c535d8a91488e3af5f07
get_tx_keys 6b4099c14803fe8702ffe3e17c9809152c9f5a2e593a4dc79b0b832143393c72 b043741597bdff40e442ed468ec54e26b82e73bc5c4efa1faeecae9f392c322e 1536fd68a4ee7ad5143e571a8d1b8f3dc545278da72bfbfa158b95d7b886c1e4 7033e41133ec48b7d9bc433a5e5891fc2fa8ae52456d2fb66b92b069c7435404
get_tx_keys 730d60e2b1ffcbf1993416d239b1962af22fef6487574b062b9993408842fa99 a0c7a51e991381d29a2ea8f946e13ecf49bee22c6f21ecb352fd962bb35073ed e2de50d99828c98d2e26c752e243a681fd047ce7a704de6a4a007fcf7f914259 dff7e6a797d4a32df274445d3a9417d6efb9f9c2a5a020775fde0ca5d7d93a0a
get_tx_keys f0c14c8737342db317dc26df3d89671fd7b73aec00c338cdaee4d907d451905a 4eca743396934cd3ecef0fb3d494468e9f7dc80ef0e9a2f384adad259bb3acec 65dea1840862814efd757e6119f417ba776749540e2f7c438c2b70b6c22b5f3f 51d03a1ae2176c8187fa08bba68575e08b93be82dc12ef4f8761b1d3f778e20c
get_tx_keys b0f032801c81bcf837a9b2e23bcb2f8e2d4df7f95dcfc161ee60bd5a80c34044 f9c11bd430d27bbf71c4955f6d9e46fa53824cd8d5fc142f0fcbaf49b59b8c31 c6f2e32aa5b5ccb144ae42b1761fb870c7bf98b23e20c4520aea8f0dea714fcd 38e28ce5bbea9e8b807d9c74fe0aff7650c2989ccea55b6ba4fbb93ef5c36b07
get_tx_keys 593bc6d239523d218dfe55504869ebd7ad29b13d30a5a11de26fe58cb8855479 81db8d71a3c321f15f0ce0dd74df0e1971c2b59c6ad6f43a708a25fd854fd00d a3274fa896106c3682cd1f4a8c7c156083c4ed56b4b8bfd09232928a9a78b82a 85585745f14815c7d87c7da08e8998f497f1e861ef729d7d86a0096408d0270f
get_tx_keys 3619f547023deb2f111165c3bf3f36c1ed69e32b7f4b4fa97076397e0c489319 8933bb951525735f4bd1e7ce453e92d8b54eb74e3789acb4f1e5c88de8660d7c 0fdde96aeba654199d217f08abaf5013fb18e3907b9bc70c03644399e70e6619 5820bb2cf8acdda1275c4dd9c0ac43d613f58ffa7bd7af9d1bf3269c5fed7b09
get_tx_keys 3bc0bf75e710c37bf4d3128a84cd855e17ce607e4d8cf62c6095d7797ebe6f91 267e2b0df6439152aaed566eedbd0e6fb193707163b767d9df00cbf33f3bf464 3a13e6e8b664de002234f4b3d134002caa5dfa6ba169c0130833a920eeac5e69 7be8e0b24f4a3e7956f398d7e0964a96870e6f00063def899a66939946d76a0b
get_tx_keys 346fb50aed359a896393d1914a5ddcdd450b3dcd53490071ad1e1206cea38a7e ebc9697ca939787c56e90df95cc1c72db7ed2249d3006c6fed8d628798bf1287 8e14f8f953f0744eda2dd5c568aaab445d7e425be9280ea00eec22e779f1ec09 1696e2b080a1073e937ba10bfce9b116791a2b494183b49c13e21dc5362b5107
get_tx_keys afc4721f844d9fbf35c47d8f97a769b4d869ccde5ed4905f01e4378c4fcc1050 47ede7e9a14b9e26589998d3fb5de17d40054a2b4d5ee803fa9447151f2abf3d 9fac272f57d3c2367cd95bf4eadad844ab3448cac9f4aa11c5df798881075740 2288136c0e00de4d6152fd23a898f935a4fdec6d1d86854aa0c6fdaa66930709
get_tx_keys 13f49086eee9b4caed107ec87b561e676d4e6c47fa290fa93849bb1b6972d5fa 411367507f9e482dd2ef0797f2b9602885e16526657b39a4552288a6a924cf56 2f1b225080e69488c9afc374754198b4289659027ffb9edcbe18bbf173573ee0 8760812d9d5cc0eb8ce95f0617739223061b07c6f51521eaa8433a0fdf45c20f
get_tx_keys 3b25ddf310280d8bd911c98c60ffaf93f01784c941212fbe6ac1e5744479801d 0aa509792c094d94070ec6702541523c7f8b3ab69724836c0f4a23d8cb9918cc ca7051e43bfbedc5ea4dcad7581875838bfa2daac6f26691d5ed85fbbb3d5d20 dac3e60cb78e7a33447a8ff59a96097d7aa23b25020c0ec98f9f72b3880e3b0a
get_tx_keys 3a189530ed141bc0e055dc7fbdb01ed27f365d0d16a6ff49553e0211d02651f5 87a4139f5703b29e9253d7151f853b7c5b5c654efda2328411fa32c45b0de96f 1705292b3e9f6fc28ff7cfb338fe7908352709bb63b18e183e838cbf2b470d94 d9eeb6b002c4a25a0b3ba6b9a8457fe9d6990fd9d12cdb1424aba9e3801b2c0d
get_tx_keys 87f91cbbb4409ecb9e8fb7e4777ca92a4ffcd001e6254d5e39d9f05dce677775 988e2aca31f8e292783c8a698e8b71d3f239fd7c94c05bee96a91f9f458ffda4 90ea8e86cc8959fe5c2f50874551032432fc1cc5fc1c5af40bfec71ee7b6cc76 83b3c2e6f87def56949e1beac36d467a91a2d8b6ffbed393bd45f3ac5867b10d
get_tx_keys e3542c51b61a3d9eb0773335582a8d6dd24c32413a969d5cb9629ddc77a8e49f c7d7ce6d43ec125071b8c5115f010164b89a33eb4aedf4ca31700a975177c617 df5476bf0fa51deeda6e0ac0c48e0fd64e8d3abf9b0e60572c757afebe2bb71a ef2c6830783a181234a503da9308a158c47f97d0da0c5b612a8ea67b4ec3e708
get_tx_keys dfffcad8d938c771497214aad51e5f8e307b9cfb5bf7f37cfcf73ae19344aa50 1e117af98c71f5443715d961d0d0d4436e2451be401db8ad40e2a66de65dbc0d a165ebbedf785963098865897d62821c72e0d89afbeffad3b54012745be4cd78 3ae76a2dfd4acc0e7bb7cfbdd516e269a2294005b1bc877f2feeaf52ac2aa70c
get_tx_keys cabfa01f1fed98842b2a30a547d34c5be1fbaa1c5753d83519b59f79239956c7 0faf698d4917c7edccb1dc401e9b33d775d6d699eff51ff6a29ca5da2d9df969 4021cad4e8a45714bc893475487d8b07c868bbddd7086f5befaadc92fca9e892 2417b7887c6f287775d736d0fbe1544d53e2ed33adc948ceca8c16f0b14b2d05

@ -16,6 +16,8 @@
*/
#include "common.h"
#include "json_parsers.h"
#include <rapidjson/document.h>
#include "gtest/gtest.h"
#include <random>
#include <sstream>
@ -146,6 +148,33 @@ TEST(difficulty_type, input_output)
test_value(std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), "340282366920938463463374607431768211455");
}
TEST(difficulty_type, json_parser)
{
auto test_value = [](uint64_t lo, uint64_t hi, const char* s) {
difficulty_type diff{ lo, hi };
std::stringstream ss;
ss << "{\"diff\":\"" << s << "\"}";
using namespace rapidjson;
Document doc;
doc.Parse(ss.str().c_str());
difficulty_type diff2;
parseValue(doc, "diff", diff2);
ASSERT_EQ(diff2, diff);
};
test_value(0, 0, "0x0");
test_value(1, 0, "0x1");
test_value(0x123456789abcdefull, 0, "0x123456789abcdef");
test_value(0x123456789abcdefull, 0, "0x123456789ABCDEF");
test_value(std::numeric_limits<uint64_t>::max(), 0, "0xffffffffffffffff");
test_value(0, 1, "0x10000000000000000");
test_value(1, 1, "0x10000000000000001");
test_value(0x1122334455667788ull, 0x99aabbccddeeff00ull, "0x99aabbccddeeff001122334455667788");
test_value(std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::max(), "0xffffffffffffffffffffffffffffffff");
}
TEST(difficulty_type, check_pow)
{
hash h;

@ -16,6 +16,8 @@
*/
#include "common.h"
#include "json_parsers.h"
#include <rapidjson/document.h>
#include "gtest/gtest.h"
#include <random>
#include <sstream>
@ -90,4 +92,36 @@ TEST(hash, input_output)
check(h, "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0");
}
TEST(hash, json_parser)
{
auto check = [](const hash& h, const char* s) {
std::stringstream ss;
ss << "{\"hash\":\"" << s << "\"}";
using namespace rapidjson;
Document doc;
doc.Parse(ss.str().c_str());
hash h2;
parseValue(doc, "hash", h2);
ASSERT_EQ(h2, h);
};
hash h;
check(h, "0000000000000000000000000000000000000000000000000000000000000000");
memset(h.h, -1, HASH_SIZE);
check(h, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
for (uint8_t i = 0; i < HASH_SIZE; ++i) {
h.h[i] = i;
}
check(h, "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
for (uint8_t i = 0; i < HASH_SIZE; ++i) {
h.h[i] = 0xff - i;
}
check(h, "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0");
}
}

Loading…
Cancel
Save