Skip to content

Stealth Quantum PoS (qPoS)

StealthSend edited this page Apr 25, 2019 · 8 revisions

Stealth Quantum Proof-Of-Stake (qPoS)

© 2018, 2019 JAMES STROUD ALL RIGHTS RESERVED

WARNING

This document is still a draft. This document and the specification it describes is likely to change without notice. The implementation, if one ever exists, is likely to deviate from what is written. The protocol proposed herein may never be implemented and may not work as intended. In fact it could cause users a total loss of investment or funds if implemented, or if any attempt is made at implementing it, or if the attempt is a failure, or if no attempt is ever made. The existence of this document is not a promise nor even a suggestion that any specific action will ever be taken by any individual. Feedback is welcome. Use XST at your own risk.

Abstract

This whitepaper describes a novel type of blockchain consensus intended to be utilized in Stealth (currency symbol: XST). Consensus in Stealth will be decided by a round-robin block certification system that is purely economic, where block rewards are results driven. This system, called qPoS (quantum proof-of-stake), has a group of signatories that produce blocks in rounds. Signing privileges are tokenized as digital assets, called stakers. As signatories produce blocks, their stakers gain not only influence on consensus, but also earning capacity. Both increase with the square root of the net number of blocks produced by the staker. Existing stakers are transferrable, and new stakers can be purchased from the blockchain through a process that eliminates the purchasing money from the XST supply.

In qPoS, block signers (stakers) are queued at the beginning of each round. When a given staker has its turn, the staker has five seconds to sign and submit a block to the network. If the staker does not produce the block during this allotted five second interval, the staker is penalized with a "miss" that reduces by one the net number of blocks the staker has signed. Additionally, the staker cannot claim a reward for a missed block and may be subject to termination based on rules described below.

The ruleset for qPoS enforces that stakers will be rewared for not only producing blocks, but for a history of good behavior. Good behavior is quantified by a metric that herein is called "weight". Weight is calculated as the square root of the net number of blocks a staker has produced over its lifetime. This highly economic and results-based system stands in contrast to systems described as delegated proof-of-stake (dPoS). Unless carefully planned and conducted, dPoS signing rights rely on the political standing of the signatories. These political blockchain systems are susceptible to corruption in that signatories may gain favor with parties who have overwhelming stake originating from insider status, leading to problems with wealth distribution as well as blockchain performance.

Introduction

Several Approaches to Scaling

A central challenge of cryptocurrencies like Bitcoin is to support enough transaction throughput to scale with worldwide use. The several recent contentious forks of Bitcoin are symptomatic of this challenge in that Bitcoin has difficulty scaling with its popularity, even as global adoption is far less than 0.1%. At about 240 bytes per transaction, Bitcoin's throughput has historically been capped at about 7 transactions per second (7 tps). Bitcoin transaction fees can be several USD, and in some cases may take days to confirm. As a result of Bitcoin's difficulty scaling, much disagreement has arisen in the Bitcoin community over how scaling problems should be addressed. The solution adopted by Bitcoin is known as segwit (segregated witness) and has provided marginal benefit. Forked solutions include BitcoinCash, BitcoinGold, BitcoinS2W, all of which offer small improvements with no new scaling properties over the original Bitcoin protocol. Mostly, these solutions (1) trim blocks by culling information (segwit), or (2) merely increase the block sizes (BitcoinCash), or some combination thereof (BitcoinS2W).

More promising solutions to the scaling problem are varied, and some have potential to be very successful. Of note are projects like NANO (formerly RaiBlocks), IOTA, BitShares, and Ardor. Each offers a slightly different approach to scaling.

NANO, for example, uses a so-called block lattice wherein every node has its own version of the blockchain, meaning all copies are legitimate but not necessarily equivalent at any moment. Voting representatives approve transactions. The voting power of a given representative is derived from the total amount of NANO proxied to it. Proxied NANO is spendable, and once spent, the voting power of the NANO becomes automatically proxied by the new owner.

A critical aspect of the NANO protocol is that the task of ordering transactions is required only of nodes that send and receive transactions. A given node must only order those specific transactions for which it is responsible. These requirements obviate the need to collate transactions into blocks. Consequently, NANO enables practically unlimited transaction throughput with no transaction fees.

On the surface, NANO has one theoretical tradeoff in that voting representatives apparently perform their jobs altruistically. This altruism is limited because running a representative node only costs a few dollars per month, meaning that the potential marketing value of running an prominent representative should more than compensate for the essentially negligible cost of doing so. The performance of NANO's consensus model is promising. The NANO network has been live for well over three years and NANO has been listed on several exchanges. Even though it is a valuable target with a historical market cap as high as $4.5 B, no one has demonstrated a money supply exploit on the NANO protocol.

IOTA has a related, but somewhat different parallelized solution called a tangle. A tangle is a directed acyclic graph (DAG) where new transactions must approve two previous transactions. Approval incorporates a hashcash type proof of work mechanism, giving the legitimacy of the approval process a physical basis. One tradeoff in the IOTA protocol is that any devices creating transactions must (1) have global knowledge of the tangle to validate that a particular transaction does not double spend, (2) collectively have enough computational power to avert a 34% brute force attack. The first requirement can be met by trusted nodes that have enough memory resources to keep track of the global state. The second is somewhat more difficult to meet because the IOT (internet of things) devices are not optimized for energy consuming proof of work calculations. For the time being, IOTA defends against 34% attack by regular publication of checkpoints called "milestones", which are issued by a server called a "coordinator", that is controlled by the IOTA foundation. It is not yet known with certainty that the IOTA network will ever grow large enough such that the centralized coordinator may be abandoned.

BitShares approaches the scaling problem not through parallelization, but through optimization of single-threaded block certification. It uses what is known as delegated proof of stake (dPoS) to purportedly enable 100,000 tps. In dPoS, the signatories, called "witnesses", produce blocks according to a pseudo-randomized queue. Because a witness knows precisely when it is required to sign a block, the witness produces blocks without any competition from other signatories. This lack of competition at the time of block certification makes the process vastly more efficient than Bitcoin's competitive proof-of-work system. Importantly, dPoS witnesses are chosen by stake-weighted voting. As judged by the long term performance of the BitShares network, this system is very robust, producing very fast 3 second block spacings that come at highly regular intervals. BitShares requires transaction fees, but a derivative project, called Steem, does not, although Steem requires users to purchase stake in the platform. In Steem, this stake comes with a bandwidth allocation that allows users to make transactions with no fees.

The most important consideration of dPoS is that stake-weighted witness voting means that flatter distribution of stake will result in more robust witness selection. Thus, distribution must be carefully conducted or the witness pool will be corrupted by politics. In practice, a flat distribution is difficult to achieve at the outset without careful planning.

Using a Hybrid Ledger to Improve Economics and Throughput

Distributed ledgers with very high throughput, like NANO and BitShares, track value using an account registry, where each registered account has an associated balance. These types of ledgers are called "state ledgers" herein. In a state ledger, a transfer simply means that one account is debited the amount of the transfer, while a different account is credited an equivalent or smaller amount. Any difference between debits and credits is taken as a transaction fee by the transaction processor. Each transfer can be considered a "state transition", that changes the state of the ledger. Upon the inclusion of any given block to the blockchain, a state ledger has a specific state. In this context, the word "state" refers to the state of all balances of all accounts in the ledger.

To make a concrete example of the concepts of "state" and "state transition", imagine a state ledger with two accounts, Alice and Bob, with $100 and $50 balances, respectively. The state of this ledger could be expressed succinctly as: Alice=100,Bob=50. If Bob sends $10 to Alice, then the state ledger undergoes a state transition that could be expressed as: Alice[+10],Bob[-10]. And the new state would be: Alice=110,Bob=40.

State ledgers are efficient in that all account balances reside in a single data structure that changes every block. Because a state ledger stores only account balances, the ledger data structure itself is not a historical record of transactions, although the history is recorded in the blockchain as a series of state transitions. Note that it is only necessary that the blockchain stores state transitions, with no need to store any state information. The state at any time may be reconstructed by replaying the state transitions.

In contrast to state ledgers, Bitcoin and many other cryptocurrencies utilize what is known as a UTXO (unspent transaction output) ledger. A UTXO ledger works somewhat like traveler's cheques, where unremitted cheques may be spent by the addressee. Each cheque is analogous to a UTXO. A cheque (or UTXO) is spent by signing it, rendering it unspendable (unable to be spent). The spender then writes new cheques addressed to recipients, and totaling the amount spent (ignoring fees, which will not be considered for this discussion).

An example is that Alice has a cheque (UTXO) for $10 and needs to send $7 to Bob. Alice signs her $10 cheque (spending it), writes Bob a cheque for $7, and herself a cheque for $3 (which is her "change"). In blockchain parlance, the $10 cheque is an "input" and the $7 and $3 cheques are "outputs". The original input is now split into two outputs. Note that the set of unspent outputs does not necessarily grow every transaction because multiple inputs can be combined in any given transaction.

The main operational distinction between state ledgers and UTXO ledgers is that the concept of a transaction is fundamentally different between the two. In a state ledger, a transaction specifies a state transition, whereas a transaction in a UTXO ledger may be considered a bearer instrument in that the owner of the instrument may transfer the value of the instrument to another individual, creating a new instrument in the process. This means that the state of a UTXO ledger must be calculated by tallying over all UTXOs, while somehow assigning each UTXO to an account.

For Bitcoin and similar UTXO ledgers, the task of account assignment can be tricky if not sometimes impossible, because each UTXO does not have a specific owner. Instead UTXOs specify those conditions under which the value the UTXO may be spent. The requirements for spending may be arbitrarily complex, specified in what is known as a spending script. UTXOs may be locked until a future date, require a subset of signatures, or even require the spender to provide a solution to a cryptographic puzzle.

An important feature of UTXO ledgers is that the complexity of UTXO transactions allows for cryptographic obfuscation of ownership. To understand this feature, we first consider state ledgers. State ledgers consist of accounts with balances. For an account to be added to a state ledger, it must effectively be registered with the ledger. For some cryptocurrencies, like BitShares or NXT, accounts are registered by specific registration transactions. For other cryptocurrencies, like NANO, the account is effectively registered upon receiving funds, in a so-called "pocketing" transaction. Because of this registration, the recipient account of any transaction is known. While the person who owns an account can not be determined simply by analyzing the transactions, all transactions, and their values, can be linked to specific accounts, greatly reducing the privacy offered by state ledgers.

In contrast, no such registration is needed for UTXO ledgers. Additionally, the potential for arbitrarily complex spending conditions means that sophisticated cryptographic proofs can be used to hide which UTXOs are being spent. In this way, UTXO ledgers can provide complete cryptographic privacy for their users, where chains of transactions reveal no information linking the individual transactions together. This means that even if it were possible to uncover who sent a particular UTXO, say by hacking a computer or coercing a confession, it would still be impossible to determine definitively what transaction spent the UTXOs further along the chain of transactions.

The advantages of state ledgers and UTXO are different but complementary. State ledgers can be highly efficient, while UTXO ledgers offer arbitrarily complex transactions with the potential for cryptographic privacy. The XST staking protocol aims to combine the advantages of both types of ledgers to create a high throughput platform that offers complete privacy.

To do this, XST will have a hybrid ledger, partitioned into two different ledgers that work together. The first partition will be a UTXO ledger that tracks value transfers between XST users. Simply put, if one user sends XST to another user, then this transaction will be recorded in the UTXO ledger. The second partition is a state ledger that tracks stakers. This partition is called the "Staker Registry". The creation of stakers will require their registration in the Staker Registry. As stakers sign blocks, their rewards are reflected in the Staker Registry by incrementing balances. These rewards are denominated in XST, which can be transferred to the UTXO ledger through a special transaction called TX_CLAIM, discussed below. The use of a state ledger for staking allows efficient consensus, wherein it is possible for block times to be very fast (5 seconds) and regularly spaced (less than a 1 second standard deviation in block spacings).

The two component ledgers of the hybrid interface through two operations, OP_PURCHASE, and OP_CLAIM. OP_PURCHASE is the operation wherein an XST holder may purchase a staker from the network using funds from the UTXO register. This operation destroys XST and creates a staker. OP_CLAIM is the operation where a staker owner transfers XST rewards from the Staker Registry to the UTXO ledger.

The remaining part of this whitepaper describes the XST consensus protocol in technical detail.

Staker Rules

Stakers are subject to the following rules:

  1. Anyone can buy a staker from the blockchain at any time.
  2. Stakers are bought with XST that is permanently removed from the money supply upon execution of the purchase.
  3. Stakers cost P = M*floor(log2(42+N))/4000 + 200N, where M is the money supply and N is the number of extant stakers. In other words, the price of the first staker bought will be calculated with N=0. The log2 term is log base 2. For example, the first staker will therefore cost 38,750 XST if the money supply is 31,000,000 XST.
  4. Stakers can not be liquidated to XST through the blockchain, but they are transferrable, allowing for owners to liquidate them through private sales.
  5. All stakers are not equal. They gain consensus influence and reward potential as they produce new blocks. Both influence and rewards increase with the square root of the net number of blocks they have produced. This dependence on the net number of blocks produced means that a history of good behavior is strongly incentivized.
  6. Staking occurs in rounds, where each active staker produces one block per round. As described below, stakers can be terminated. Terminated stakers are permanently barred from producing blocks.
  7. Rewards are weighted according to the integer part of the square root of the net total blocks produced during the lifetime of a staker. The implementation of the square root function is described below. This implementation is chosen for efficiency, platform independence, and consensus-friendliness in that it does not rely on floating point arithmetic.
  8. The minimum weight for a staker is 1.
  9. The inflation rate is 1% of the total money supply per annum, compounded every round. The reward for a staker is assigned according to the formula w * R / W, where w is the staker's weight, R is the total rewards to be distributed for the given round, and W is the sum of all stakers' weights. If 60 stakers exist, a 1% inflation rate means each staker will earn about 8% per year on the initial price. If 30 stakers exist, each staker will earn about 16% on the initial price. Since these rates represent excellent return for any asset, it is expected that between 30 and 60 or more stakers will be active at any time.
  10. Rewards for a round are calculated according to the formula M*n/(100*Y), where M is the money supply at the start of the round, n is the number of stakers in the round, and Y is the number of blocks in a year, given 5 second blocks. The 100 in the denominator is the reciprocal of 1%.
  11. Because rewards are recalculated each round, all stakers get exactly the same weighted reward in a given round. Specifically, there is no benefit to being first or last in a round.
  12. The order of each round is determined by nested hashing of the last block of the previous round. To make it difficult to influence ordering, the hash of the block immediately preceding a round is further hashed using 16 rounds of X13 to seed a Mersenne twister pseudo random number generator (MT-PRNG) that assigns the ordering by shuffle.

Key Data Structures

Understanding qPoS requires familiarity with its hierarchy of data structures, consisting of the registry at the top. The registry contains and controls the stakers, the queue, and a ledger of balances.

Stakers

Stakers are unique assets authorized to sign blocks according to the qPoS scheduled block production protocol. The most critical data elements of stakers are the three authorities (owner, delegate, and controller) discussed below.

Stakers are identified by unique positive integers, starting with 1 and assigned sequentially. Each new staker will be assigned the lowest unused identifier. Identifiers cannot be reused, so must be unique among active stakers.

Stakers are also identified by a perpetually unique alias, which allows for more user friendly software interfaces, such as blockchain browsers.

Stakers can be created and disqualified at any time. Disqualified stakers are deleted from the registry at the end of each round.

The Registry

The registry stores stakers, which are added to the registry upon their creation (purchase from the blockchain) and deleted from the registry when they are disqualified. To encourage rapid withdrawal of earnings upon disqualification, the balances of terminated stakers and their delegates (discussed below) will be reduced by 1/310*10^9 (one 310 billionth) of the block reward every round. For example, if the money supply is 31 million, then the balances of any terminated stakers will be reduced by 0.0001 XST every round.

The registry allows for account-based optimizations of block signing and the assignment of block rewards. For example, the block signature does not need to include the public key, which is a minimum of 21 bytes. Only 4 bytes are needed for a reference to the staker's unique ID in the registry.

The Staker Queue

Derived from the registry is a simple vector (list-type data structure) of identifiers of active stakers. This vector is first sorted in ascending order of staker ID, then shuffled using a deterministic MT-PRNG to obtain the staker queue for a given round. The queue, therefore, is essentially this vector that holds the shuffled stakers for the current round. The queue is created at the beginning of each round. Along with the queue is a simple index that points to the current slot. This index is 0 at the beginning of a round incremented after each 5 second slot, whether or not a block was produced during that period. A slot is not complete until the registry is updated. When the index is equal to the length of the queue, the round is over, the queue for the next round is generated, and the index reset to 0.

The Registry Ledger

To significantly reduce unspent outputs that bloat the blockchain, block rewards are added to the ledger in the staker registry. The minimum claim will be 1 day of block rewards, meaning an account in the ledger may not make a claim more frequently than once per day. Claims create new unspent transaction outputs (UTXOs) on the blockchain.

These optimizations allow for very rapid blocks, minimal resource usage of empty blocks, and a smaller UTXO set that is not polluted with block rewards.

Price Discovery

Although the purchase price of a staker is essentially fixed, the sale of stakers can be thought of as a reverse auction wherein staker buyers sell a fixed amount XST for a varying fraction of the reward pool. Each additional staker further divides the reward pool, lowering the selling price of XST relative to the rewards. Because a reverse auction can have open bidding, it can be conducted entirely on the XST blockchain without the need for escrow, elaborate cryptographic schemes to hide bids, or tranches. Since bids are binding, this type of auction will reliably find the market price for the reward pool, as valued in XST.

Stake Weight

In the case of forks, the winning chain shall be decided by a weighted participation score, called chain trust. Weighting is calculated as the integer part of the square root of the net number of total blocks minted by the staker prior to the current round. The square root is taken as described. Source code and notes are given in the Appendix herein. For each block, the weight of the staker that produces the block is added to the prior block's trust score. The chain with the highest trust, and that also conforms to all aspects of the protocol, will be the winning chain. Signatories are expected to produce blocks on the most trusted chain based on Schelling point game theory. In other words, except for a pentalty for missing blocks, there is no mandated penalty for signatories that produce blocks on chains that do not win. The reason is that all chains may not be known to all signatories at all times.

Staker Authorities

Stakers have three separable authorities, assigned by the public key of the secp256k1 cryptography system. The three authorities are owner, delegate, and controller. The owner authority allows for transfer of ownership and the assignment of delegate and controller authorities.

The delegation key gives block signing authority, allowing for owners to outsource staker operation to third parties. Owners may assign delegation authority at any time, permitting revocation of block signing rights if necessary. Owners may also share revenues with delegates, and the qPoS protocol will manage any payouts, making delegation fully trustless and hassle free for both owner and delegate.

The controller key allows for a staker to be temporarily disabled. A disabled staker will not be assigned slots in new queues, and will neither be penalized for missing blocks, nor compensated for producing blocks. Stakers may be disabled and enabled at any time.

Reward Balances

All block rewards are kept in an account-based ledger called the registry ledger. Accounts are assigned by public key. This system keeps balances separate from the staker data structures. Because of this separation, stakers may be deleted permanently from the registry, saving RAM storage resources. This separation also makes possible delegation and automatic delegate payouts.

Misbehavior of Stakers

Stakers may misbehave in two ways. The first is to produce blocks early such that the staker immediatly prior (the predecessor) in the queue is skipped. This early production is called a timestamp attack. The second way to misbehave is to purposefully cause forks.

Forks may result in (or be the result of) extraneous production and double production. Extraneous production is when a signatory produces blocks on two different chains, where these blocks do not directly fork the chain. Extraneous production creates orphaned blocks (called orphans) relative to a chain to which the signatory has not committed. A signatory is committed to a chain if the signatory has produced blocks on the chain in two different rounds. Extraneous production will result in a ban on chains to which the signatory has not committed.

Double production is different from extraneous production in that double production directly forks a chain and will subject a staker to an immediate ban on all chains.

Timestamp Attacks

Stakers will be discouraged from timestamp attacks by the threat of permanent disqualification. The registry will keep track of the number of missed blocks by each staker's immediate predecessor in the queue. If a staker's predecessors have missed more than 8 standard deviations from the average of all stakers' predecessors over the last 4096 blocks, then a staker will be disqualified and permanently deleted.

Extraneous Production

Stakers that create extraneous production will be banned from any subordinate chain on which they produce blocks, detected as orphans. A subordinate chain is defined as any chain that is not a staker's main chain. A main chain is defined for each staker, and is the chain on which a staker has signed two or more blocks since the last common block (CB). In principle, a staker cannot have more than one main chain because a staker that is already committed to a main chain will be banned from a second chain before the staker is able to successfully produce multiple blocks for it.

From this ruleset, it is clear that if a chain forks and a staker has produced more than once on any given chain, the staker is subject to being banned from any other chains for which it signs a block. To be banned, a staker must have (1) produced blocks more than once on its main chain since the most recent common block, and (2) produced blocks on a subordinate chain. Figure 1 illustrates the typical ban situation.

Figure 1

-> -> -> B2 -> B3 -> C1

/

CB ->
-> B1 -> -> -> -> -> C2 (SW)

Time -->

In the above diagram, the staker (named SD, for "Staker Double signing") producing blocks B1, B2, and B3 is subject to being banned from its subordinate chain (C2). In this case, the staker has committed to the main chain (C1), because the staker has produced blocks more than once on C1 since CB.

Typically, double production will be detected by a staker (called SW for "Staker on Winning chain") producing on C2 as their main chain. SW will observe B3 as an orphan, then track back along C1 and C2, discovering that B1 was produced before B2 and B3, meaning that SD produced chain (C2) to which it didn't commit. At this point, SW will terminate SD on chain C2. Termination consists of submitting a termination transaction that references B1, B2, and B3. The structure of the termination transaction will indicate that the staker will be terminated from C2. In short, any staker on a chain containing B1 will ban SD from C2.

Since C2 is SW's main chain, there is no need for SW to keep track of the state of stakers on multiple chains. SD will be terminated from SW's main chain (that contains B1). In the case that SW is actually not on the winning chain, SW will need to re-index the blockchain. Re-indexing is the process of parsing blocks in the chain, calculating the values of any stateful data structures, and building the block index.

Given the incentive to stay on the winning chain, the existence of forks should be rare, and it will be possible for all known orphans to be stored for several rounds, after which they may be pruned. Pruning means orphans will be removed from memory and from the block index in the event of reindexing.

In some cases, a staker will produce blocks on a subsordinate chain before switching to a main chain, having produced on the subordinate chain by mistake. It is worth considering such honest mistakes and assessing whether honest mistakes are likely to lead to a ban of a staker acting in good faith.

Let's imagine a scenario where a staker produces blocks on what the staker later decides is the wrong chain (C2) then switches to another chain (C1). This is the same as the scenario illustrated in Figure 1. The staker is subject to being banned from C2. However this is inconsequential to the staker because the staker has decided that C2 is the incorrect chain and the staker will not produce further on C2.

Boundary cases. The principal boundary case is when a staker produces blocks simultaneously on two chains (to the resolution of the system clock). This is illustrated in Figure 2

Figure 2

-> -> -> B2 -> B3 -> C1

/

CB ->
-> -> -> -> -> B1 -> C2 (SW)

Time -->

In this case, B1 and B3 both have the exact same timestamp. If B1 is observed before B3, it is likely that SW may not ban SD. However if B3 is observed before B1, or if SW checks known orphans when B3 is observed, SD is subject to ban by SW. If SD continues to produce blocks after B3 on C1, SD will be banned from C2.

Figure 3

-> -> -> -> -> B2 -> C1

/

CB ->
-> -> -> -> -> B1 -> C2 (SW)

Time -->

Figure 3 shows a situation where SD produced blocks on two chains simultaneously. In this case, SD is not yet subject to being banned on either chain because SD has produced blocks only once on both chains since divergence.

Switchback. In some cases a signatory will produce a block on a chain, decide it will not win, produce a block on an alternate chain, decide it will not win, then eventually switch back to a chain that becomes the winning chain. This situation is illustrated in Figure 4, where a staker produced on C2, switched to C1, then switched back to C2.

Figure 4

-> -> -> B2 -> -> -> C1

/

CB ->
-> B1 -> -> -> B3 -> C2

Time -->

Here, the staker (S) will not be banned from any chain because, at the time S produced on C1, S had produced only once on C2.

Double Work

In some cases a staker can cause a fork by producing the same block twice, with two different signatures. Two blocks are considered the same if they have the same block number, also called height, and share an immediate common block (CB). This situation is described as double signing and is illustrated in Figure 5, where B1A and B1B have the same height.

Figure 5

B1B ->

/

CB
B1A -> C2 (SW)

Time -->

Because it is exceedingly easy to prevent double signing and this behavior necessarily forks the chain, this behavior will lead to a staker's becoming terminated.

Reorganization

The stateful nature of the registry, described above, precludes chain reorganizations at arbitrary blocks. A reorganization is when a client switches to another chain on a fork. To switch, a client must replay the chain starting from the most recent registry snapshot that precedes the most recent common block. The reference implementation of the client will take snapshots approximately every hour, at the beginning of the round that immediately follows blocks with heights evenly divisible by 720. To replay, a client must find the most recent common block then backtrack to the most recent snapshot of the registry that precedes the common block. The snapshot database will be kept small by pruning snapshots older than 2 days.

Replay is very fast and computationally light because instructions for all state changes to the registry for every block are stored in RAM in data structures known as block indices. RAM storage eliminates the need for disk access, which is very slow by comparison. In other words, blocks do not have to be read from disk, nor their transactions parsed, in order to replay the registry.

Implementation

To support qPoS, several new opcodes will be added to the scripting language (called "Script"). Except for OP_CLAIM, all opcodes create non-spendable outputs. The scripting language is used for two reasons. First, the transaction script is a natural place to add information to a transaction, with precedence in OP_RETURN, which has historically been used for this purpose. Second, the scripting language comes with a validation engine that can be co-opted for qPoS instructions.

Each qPoS opcode corresponds to a transaction type with a corresponding template that can be validated by the existing Script interpreter. Names of the transaction types mirror the names of the opcodes, except for the prefix. For example the transaction name corresponding to OP_CLAIM is TX_CLAIM.

The qPoS transaction types are:

  1. TX_PURCHASE1: Purchase a staker, setting all authorities to the same public key.
  2. TX_PURCHASE3: Purchase a staker, setting all authorities to potentially different keys, and also designating a delegate payout.
  3. TX_SETOWNER: Transfer staker ownership.
  4. TX_SETDELEGATE: Assign the staker delegate and also specify the delegate payout.
  5. TX_SETCONTROLLER: Assign the staker controller.
  6. TX_ENABLE: Enable a staker.
  7. TX_DISABLE: Disable a staker.
  8. TX_CLAIM: Claim all or part of a balance from the registry ledger.
  9. TX_ACCUSEEXTRANEOUS: Accuse a staker of extraneous production. (Not implemented yet.)
  10. TX_ACCUSEDOUBLE: Accuse a staker of double production. (Not implemented yet.)

The scripts of qPoS transaction types listed above are not executed by the scripting interpreter like normal scripts. The exception is the TX_CLAIM script, that ignores the beginning of the script up to and including the OP_CLAIM opcode.

QPoS Transaction Script Templates

QPoS transactions are not prunable and only TX_CLAIM is spendable. Staker IDs are 4 bytes. Public keys (pubKeys) are 33 byte compressed keys. All amounts are big endian.

TX_PURCHASE1

Summary

Buy a staker, all 3 keys are the same.

For both TX_PURCHASE1 and TX_PURCHASE3, the name is 16 bytes and zero padded at the end because canonical pushes enforce data sizes less than OP_PUSHDATA1 (opcode 76, 0x4c) to be fixed width.

Structure

The pushdata size parameter is 57. The amount is 8 bytes.

[size, data(amount, pubKey, name), OP_PURCHASE1]

CScript Template

CScript() << OP(0x39) << OP_PURCHASE1

TX_PURCHASE3

Summary

Buy a staker, all 3 keys are potentially unique. The delegate payout (pcm) is a four byte integer representing a fractional number, expressed as percent mille (millipercent).

Structure

The pushdata size parameter is 127. The amount is 8 bytes.

[OP_PUSHDATA1, size, data(amount, ownerKey, delegateKey, controllerKey, pcm, name), OP_PURCHASE3]

CScript Template

CScript() << OP_PUSHDATA1 << OP_PURCHASE3

TX_SETOWNER

Summary

Assign staker owner key (signatory of the input must match the owner of stakerID).

Structure

The pushdata size parameter is 37.

[size, data(stakerID, pubKey), OP_SETOWNER]

CScript Template

CScript() << OP(0x25) << OP_SETOWNER

TX_SETDELEGATE

Summary

Assign staker delegate key (signatory of input must match owner of stakerID). Also specify the delegate payout. The delegate payout (pcm) is a four byte integer representing a fractional number, expressed as percent mille (millipercent).

Structure

The pushdata size parameter is 41.

[size, data(stakerID, pubKey, pcm), OP_SETDELEGATE]

CScript Template

CScript() << OP(0x29) << OP_SETDELEGATE

TX_SETCONTROLLER

Summary

Assign staker controller key (signatory of the input must match the owner of stakerID).

Structure

The pushdata size parameter is 37.

[size, data(stakerID, pubKey), OP_SETCONTROLLER]

CScript Template

CScript() << OP(0x25) << OP_SETCONTROLLER

TX_ENABLE

Summary

Enable staker (signatory of input must match owner of stakerID).

Structure

The pushdata size parameter is 4.

[size, data(stakerID), OP_ENABLE]

CScript Template

CScript() << OP(0x04) << OP_ENABLE

TX_DISABLE

Summary

Enable staker (signatory of input must match owner of stakerID).

Structure

The pushdata size parameter is 4.

[size, data(stakerID), OP_DISABLE]

CScript Template

CScript() << OP(0x04) << OP_DISABLE

TX_CLAIM

Summary

Claim rewards from registry ledger. The resulting output is spendable. Only bitcoin-style addresses (pay to pubKey hash) can be used with TX_CLAIM. The input pubKey is spent from registry ledger.

Structure

The pushdata size parameter is 41 (33 byte pubKey plus eight byte amount).

[size, data(pubKey, amount), OP_CLAIM, OP_DUP, OP_HASH160, OP_PUBKEYHASH, OP_EQUALVERIFY, OP_CHECKSIG]

CScript Template

CScript() << OP(0x29) << OP_CLAIM << OP_DUP << OP_HASH160 <<
                                               OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG

TX_ACCUSEEXTRANEOUS

Not implemented yet.

TX_ACCUSEDOUBLE

Not implemented yet.

Appendix

Weight Deriviation Function

The following function, uisqrt fully describes how weight is calculated from the net blocks n that a staker has produced.

The code is presented both for python (for readability) and C++. The function uisqrt requires that n is an unsigned int with constraints corresponding to uint32_t in C++. Thus, no check for negative numbers is performed. For comparison, two python functions are given. The first is a straightforward adaptation of JavaScript function described here. The second python function uses only integer and bitwise operations, and is the version most directly used in the C++ implementation. The C++ bit_length functions are modified from functions described here.

Python Implementation

def uisqrt(n):
  if n == 0:
    return 0
  x = 1 << int(math.ceil(n.bit_length()/2.0))
  y = (x + (n / x)) >> 1
  while y < x:
    x = y
    y = (x + (n / x)) >> 1
  return x

def uisqrt_int(n):
  if n == 0:
    return 0
  b = n.bit_length()
  x = 1 << (b >> 1)
  if b ^ x:
    x <<= 1
  y = (x + (n / x)) >> 1
  while y < x:
    x = y
    y = (x + (n / x)) >> 1
  return x

C++ Implementation

const uint8_t bit_length_table[256] =
        {  255, 1,
           2,2, 3,3,3,3, 4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,
           6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
           7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
           7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
           8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
           8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
           8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
           8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 };

uint32_t bit_length(uint32_t v)
{
    static const uint32_t ones = -1;
    uint32_t r = 0;
    if (v & (ones << 24))
    {
        r += 24;
        v >>= 24;
    }
    else if (v & (ones << 16))
    {
        r += 16;
        v >>= 16;
    }
    else if (v & (ones <<  8))
    {
        r += 8;
        v >>= 8;
    }
    return r + (uint32_t) bit_length_table[v];
}

uint32_t uisqrt(uint32_t n)
{
    if (n == 0)
    {
        return 0;
    }
    uint32_t b = bit_length(n);
    uint32_t x = 1 << (b >> 1);
    if (b ^ x)
    {
        x <<= 1;
    }
    uint32_t y = (x + (n / x)) >> 1;
    while (y < x)
    {
        x = y;
        y = (x + (n / x)) >> 1;
    }
    return x;
}