Stealth Development Blog 013: 03/08/2019
Internal Testnet: Staker Registrations and Client Restarts
This week I had two key achievements on testnet. Perhaps the most significant was that I registered a staker. I describe why this achievement is significant herein. Additionally, I debugged client restarts, so that the registry is restored perfectly after a restart. I also was able to transfer ownership of a staker. Before I discuss these landmarks, I need to revisit the topic of last week’s post regarding the structure of the block index.
–––––
Fork Dependent Structure of Block Indices
Last week I spent a lot of time discussing how full node operators would need to re-index the entire chain before the transition to qPoS. Re-indexing would take one to two days depending on hardware. This week, I am happy to say that re-indexing will not be a requirement. In other words, full node operators will be able to simply stop their client, install the new Stealth software, and start again, enjoying an otherwise uninterrupted transition to qPoS.
Previously, I mentioned that the reason for re-indexing is that it is not possible to know a block’s version (and therefore its potential content) without reading the actual block from disk, which is a slow process. I happened to be incorrect about this assertion. I overlooked that a block index is required to read a block and the block index can contain information that indicates the potential content of its corresponding block.
To explain, QPoS blocks store two additional pieces of information in the block headers: (1) the four byte block version, which is moved from the coinbase transaction to a field in the block header, and (2) the four byte staker ID of the block signer. Blocks prior to the qPoS transition don’t have these fields in the block header, making the block structure different before and after the transition to qPoS. This difference in block headers means that the contents of the blocks must be known in order to read their transactions, as I described last week. Fortunately, block indices can provide this content information.
Below I discuss changes to the block index structure that will be unique to qPoS. In short, block indices will also store all state changes to the registry so that reconstructing the registry state can be done using only block indices and not require reading any blocks from disk.
–––––
Staker Registration
The most significant achievement this week has been to register a staker. The following shows the output of getqposinfo
for the first ever registered qPoS staker:
"1" : {
"alias" : "StealthRules",
"id" : 1,
"version" : 1,
"qualified" : true,
"enabled" : false,
"weight" : 1,
"keys" : {
"owner_key" : "02b2ea2375...",
"delegate_key" : "02cd85238b...",
"controller_key" : "02cd85238b..."
},
"delegate_payout_pcm" : 0,
"total_earned" : 0.00000000,
"blocks_produced" : 0,
"blocks_missed" : 0,
"blocks_assigned" : 0,
"blocks_seen" : 0,
"previous_blocks_missed" : 0
},
The fields of this JSON output are described in SDBS 011.
Here, I have truncated the public keys from 66 characters to ten for readability. You will notice that the owner key is different from the delegate and controller keys. The reason is because I also transferred ownership from the original owner (02cd8…) to a new owner (02b2e…), representing another achievement on testnet.
Upon creating the above output, the testnet is not yet ready to make the transition from PoS to qPoS, so this staker has has 0 earnings, 0 blocks seen, etc. Notice that stakers start life disabled. This means they need to be enabled through another blockchain transaction before they can sign blocks. Enabling a staker is simple and cheap, costing only the XST transaction fee of 0.01 XST. Stakers start disabled so that the economic event (the purchase) is decoupled from the functional event (signing blocks), allowing an individual to purchase a staker at a suitable time without the requirement to have a full node ready to sign blocks at precisely that moment.
While the registration and owner transfer of a staker may seem like a simple achievement, together these abilities represent about 90% of underlying functionality of the qPoS code (consider that I spend most of my time in these SDBS posts talking about the “staker registry”).
Taking staker registration as an example, the following list outlines the key functionalities involved:
- An RPC command must communicate the purchase to the client.
- The client must convert the RPC command into a blockchain transaction, where the instructions are stored in a transaction output as a valid program in the Script programming language.
- The blockchain transaction must be extracted into a command that specifies a registry state change. This extraction happens as a prerequisite to validation.
- The blockchain transaction must be validated, including the signatory, taken from the transaction input. Also in this process, the Script program mentioned above must be compared to a Script template to ensure it is a standard transaction output. This latter comparison required some very low-level changes to the Script parser, which I won’t discuss today.
- Upon inclusion in a block, these commands must be stored in the block indices, in a fork-friendly way, meaning that early block indices do not need to have placeholders for a command list.
- Upon acceptance of a block into the main chain, these commands must be read from the blockindex and applied to the registry to create the new registry state. Critical here is to synchronize these changes with user events, blockchain events, and with the entire network, requiring careful multithreading. An example of a registry state change is the addition of a staker with all its information, as represented in the above output of
getqposinfo
. - Additionally, in order for the registry state to persist between startups, the registry state must be stored as snapshots and replayed faithfully from that snapshot to the lead block at the time of shutdown.
The list above represents the bulk of qPoS requirements, with the only real additional requirements being:
- The registry state must be rolled back upon blockchain reorganization, then replayed to the lead block of the new best chain.
- Stakers must start producing blocks upon the transition to qPoS.
- Staker owners and delegates must be able to claim earnings from the registry ledger and transfer these earnings to the blockchain (UTXO) ledger so that these earnings can be useful (e.g. sent to other individuals to buy things or sold on an exchange).
Indeed, except for finishing the testing and debugging of a few RPC commands (setstakerdelegate
, setstakercontroller
, enablestaker
, and disablestaker
) this latter bullet list constitutes the lion’s share of the remaining testing and debugging for the internal testnet. Although it seems more significant than registering a staker, making the transition in consensus mechanism from PoS to qPoS is not as technically challenging as registering a staker. In terms of assembling blocks and signing them, qPoS is very similar to PoS. The only technical differences are that qPoS block signers are scheduled in a queue that is managed by the registry, while PoS block signers participate in an inefficient stake lottery known as King-Nadal consensus.
Restoring the Registry State after Client Restart
Next to blockchain reorganizations, one of the most technically challenging components of maintaining the registry is to restore its state after restart. At first glance, this may seem as simple as storing the registry state on shutdown then reading it again on startup. In a perfect world, this approach would suffice.
However, in reality, running a full node is fraught with the perils of unexpected program terminations. These events can range from power outages, to crashes related to bugs in the code (nobody is perfect), to hardware failures. When a program crashes it cannot be expected to store information in anticipation of a shutdown. Instead, either this information must be periodically stored as a matter of housekeeping (i.e. snapshots), or must be easily reconstructed (a.k.a “replayed”) from information that is stored during normal operations (e.g. storing block indices).
In qPoS, I apply both of these latter strategies. Every 720 blocks (one hour), the protocol stores a snapshot of the staker registry to disk. Upon restart, the newest snapshot that is older than the lead block is read and the registry is replayed from this snapshot, saving replay time. To make this replay more efficient, qPoS stores information for registry state transitions in the block indices. Note that a new block index is stored to disk upon the processing of any block, including orphans. This state change information is stored as a set of imperatives in an vector (array-like) data structure called “vDeets” (short for “vector of registry state-change details”). These imperatives can be applied to the registry with minimal processing. Adding these imperatives to the block indices was one of my major improvements to qPoS this week.
–––––
Hondo
Congratulations @stealthsend! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :
Click here to view your Board
If you no longer want to receive notifications, reply to this comment with the word
STOP
To support your work, I also upvoted your post!
Vote for @Steemitboard as a witness and get one more award and increased upvotes!
Congratulations @stealthsend! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Do not miss the last post from @steemitboard:
Vote for @Steemitboard as a witness to get one more award and increased upvotes!