Super Staker Pool

Jack Winter
Qtum
Published in
9 min readAug 25, 2021

--

Offline Staking address delegations to a Super Staker provide payouts according to the Proof-of-Stake algorithm: block rewards are based on estimated time with much variance. Payouts from this Super Staker Pool are made daily according to the delegated address percentage contribution to the pool. This Pool is a proof of concept for a 100% fee staking pool, to work out a simplistic data structure and node interface functions based on how Qtum Proof-of-Stake, Super Stakers, and block rewards work.

This Super Staker Pool was run as the official pool during the Testnet Stake-A-Thon (July 25 — August 24, 2020) and has run more or less continuously since then on Testnet.

TL;DR the Pool Payout Manager is a Python script that works with a Qtum Super Staker. Addresses delegated to the pool (with a 100% fee) receive a daily payout based on their share in the pool. This blog describes the data structures, design tradeoffs, and algorithms used to operate the pool.

Solo Stakers vs. the Pool

Solo Stakers or delegated addresses stake with their UTXO balance and win block rewards based on the Proof-of-Stake algorithms which can mean days or weeks between the rewards (depending on wallet size) with considerable variation. The “expected time” can be seen as a long-term average over many block rewards.

In contrast, the pool makes a payout to every delegate every day based on:

  • A delegated addresses share of the pool as each block reward is won.
  • The amount won in block rewards by the pool each day.
  • After the pool fee is subtracted, a minimum amount of 0.001 QTUM, or else that “poolshare” is carried over to the next day.

For this pooling approach, the addresses must be delegated with a 100% fee to the Super Staker. The Super Staker receives the entire block reward for the pool and the PPM (Pool Payout Manager — Python script) calculates the share for each pool member (less fees) and sends it out at the end of each day.

With the 100% Super Staker fee, this pool is not fully trustless (as are most pools) although Qtum address delegation is non-custodial (delegates keep full control of their coins). If the pool just kept all the block rewards and didn’t make the daily payouts the delegated addresses could remove their delegations or send their coins to another Super Staker.

Pool Setup Considerations

Next, we review some setup considerations for the pool.

Vanity Address

To pick a nice vanity address, use the commands

keypoolrefill 500000
dumpwallet “/home/jack/Desktop/Addresses/1”

to get 500,000 addresses and run the commands a dozen times or so. This generates quite a bit of data but you can search for interesting addresses using a log editor such as glogg. Searching for “qPool” gave the address used which was “qPoolFF…”.

Super Staker Setup

The default Super Staker will stake UTXOs for delegated addresses with a minimum size of 100 QTUM. I wanted the pool to be friendly to little delegators and set the minimum UTXO size to 25 QTUM. The launch script for the qtumd Super Staker is (this is one line):

qtumd -testnet -superstaking -stakingminfee=100

-stakingminutxovalue=25 -txindex

Here the parameters specify the testnet blockchain, super staker fee of 100%, delegated address minimum UTXO size of 25 QTUM, and CLI (Command Line Interface) access to any transaction ID on the blockchain.

Linux Box

.

The testnet pool ran on a fast Linux box (Intel i7 CPU, 16 MB RAM, 1 TB SSD) with a dedicated 1 kW UPS (Uninterruptable Power Supply). In production, the pool should be run as a cloud VPS (instance of a Virtual Private Server).

.

Software Design Tradeoffs

The PPM Python script is a basic proof-of-concept application to manage the Super Staker Pool. It does not necessarily have the best data structures or algorithms, but they work well to demonstrate the interface and algorithms needed to run a qtumd Super Staker as a pool.

Next, I review some design issues and workarounds for the pool. Code and documentation are given in the GitHub repository, with some additional documentation details given in the references below.

Changing Delegations

As with Qtum offline staking, there are no lockups or restrictions for the pool delegation. Delegates may join or leave the pool at any time or increase/decrease their allocation to the pool at any time. The pool calculates the poolshare for every delegate for every block reward based on the mature UTXOs in the pool when that block reward is won. This allows delegates to join the pool during the day, or even fully exit the pool during the day, and still receive their fair poolshare for each block reward where they delegated mature UTXOs. For more description about this see PPMU (Pay Per Mature UTXO) in the references below.

Persistent Memory

To track the poolshare allocations and revenue between startups of the PPM (or qtumd) the PPM writes a file for each block reward to disk with the poolshare allocated to each delegate up to and including that block reword. On restart (after an update to PPM, qtumd, etc.) the PPM will load that latest block reward file to import all the previous values, then read all the following blocks to catch up the pool (for the case that PPM was reset while qtumd continued staking, potentially winning block rewards).

Data Structures, Carryover

A table (array) is used to manage the delegates and their weights. After each block reward, the poolshare for each delegate is calculated based on their mature UTXOs for that block reward. Here is an example showing 4 delegates with 25 QTUM up to 90,000 QTUM winning a pool block reward for 1.0 QTUM. Note a block reward won by the Super Staker’s UTXOs does not add to the pool rewards, only block rewards won by the pool delegates do.

A pool block reward

Here we see the pool receiving a block reward of 1.0 QTUM and every delegate has their weighted share added to their poolshare value.

At the end of the day, which follows immediately in this example, the poolshares are paid out, less the pool fee, for any amount greater than 0.001 QTUM (0.001 is the minimum size for the sendmanywithdupes command used). For a delegate with < 0.001 QTUM, that amount is carried over to the next day, which looks like this:

New day, carryover

Here the delegate Qabc… with 25 QTUM didn’t meet the minimum payment of 0.001 QTUM (after the fee would have been deducted) so their poolshare was carried over to the next day.

Here is an example of a recent payout transaction https://testnet.qtum.info/tx/19768fdb3ac8f472929d7183ff5fdf85ae8c2f495973a0e67058aae0fba57626

To give a calculation example for the 25 QTUM delegate, if they were in a pool of 2,500 QTUM size, the pool would win about one block reward a day (at current ROI) with a daily payout of 0.009 QTUM (after the 12% fee). This payout for the 25 QTUM delegation would stay the same (on average) in larger pools because the math scales linearly.

Pool Fee

Unlike regular Super Stakers, where it is very difficult to change the fee once delegations are received, the pool can easily change the fee on startup in the PPM’s configuration file. The pool faces higher costs with transaction fees for the daily payout, perhaps half a percent. The pool fee can be set to zero or even a negative fee (payouts are higher than the normal poolshare) for a promotional period. In the example above and for the Testnet pool, the fee was 12%. Note this pool fee of 12% is the fee charged by the pool and should not be confused with the 100% delegation fee.

Orphans

A significant design issue for the pool was dealing with orphans. An individual node makes its best effort to reach consensus and publish blocks, but by design has a very self-centered view of the blockchain when it publishes a block (and gives itself the block reward). Only several blocks or minutes later will it learn that its block was an orphan.

The PPM uses CLI (Command Line Interface) via Python to monitor and manage the qtumd Super Staker. An early orphan workaround attempted to use qtum.info API calls to augment the qtumd node interface and help answer the question: who won that last block reward? Although the qtum.info API is very reliable it might be offline for an upgrade or maintenance and wasn’t a good match for the 24/7 Super Staker.

Also, sadly, qtum.info can’t resolve orphan blocks in real-time because it is looking at the blockchain through its own nodes, and nodes by themselves can’t know about orphans in real-time.

Self-published orphan blocks are problematic for the PPM. For every block the node reaches consensus and publishes a block the PPM must add the block reward to the pool share, and add the share for each delegate, only to find out a block or two later that the block was an orphan and have to revert all the block reward shares. The node can revert orphans gracefully but would be too complicated for the PPM (and beyond the proof-of-concept scope). Also, this approach would show a real-time reduction in the pool balance. I eventually decided the PPM should live in the past.

To avoid reverting orphan block rewards, the PPM works 5 blocks in the past, which is sufficient time for any orphans to be resolved. For example, when the chain height is 1,500,000, the PPM will process block 1,499,995, always trailing by 5 blocks.

Web Monitor

The PPM provides basic proof of concept code and qtumd interface functions. A future enhancement would provide a Flask Web page for monitoring and promoting the pool. Such a Web interface could show real-time pool size, pool block rewards won, pool revenue, and estimate daily payouts. It would not need to manage user accounts since these are permissionless through offline staking delegations.

References

1. GitHub Pool Payout Manager Repository https://github.com/JB395/Pool-Payout-Manager

2. Readme.md documentation file from repo:

About PPM

PPM is a Proof-of-Concept program to monitor qtumd operating as a 100% fee super staker (a pool), tracks and makes payouts, sends emails, and logs activity. PPM uses qtum-cli to send CLI queries to the qtumd server application to identify staking events, and log various activities of the super staker. PPM sends a query to check for a new block approximately every 4 seconds but will wait for 5 confirmations before checking a new block (to let orphans settle out). PPM will require qtumd to be running and staking enabled (decrypted for staking only) or will stay in an error loop until these two conditions are met.

As a Proof of Concept, PPM uses many global variables and simple (linear search) arrays for data storage (the delegates and their accumulated payout). Data responses from the node are typically stored in the global variable “data”, which may then be parsed by the main program or various functions. The principal arrays are delegateArray[] which stores the delegates for this super staker, and poolShareArray[] which stores the payout accrued by each block reward for the delegate. Delegate payouts must reach a minimum of 0.001 QTUM, (configurable) or else they are carried over to accrue in the next period.

Pay Per Mature UTXO (PPMU)

Delegated addresses accrue payout for each pool block reward based on their address’s mature UTXOs (weight) of minimum size accepted by the pool, divided by the overall pool weight for that block reward. Individual pool members may add or remove UTXOs from their delegated address at any time, join or leave the pool at any time, and all pool members are being treated fairly according to the mature UTXOs staking for each block.

Directory configuration

--

--