Skip to content

Stealth Quantum PoS (qPoS)

StealthSend edited this page Apr 30, 2018 · 6 revisions

Stealth Quantum Proof-Of-Stake (qPoS)

© 2018 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 that will be utilized in Stealth (XST). Consensus in XST 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 are queued at the beginning of each round. When a given staker has its turn, the staker has 5 seconds to sign and submit a block to the network. If the staker does not produce the block in this time, it is penalized with a "miss" that reduces by 1 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 block chain 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 block chain, 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 only needs to 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 two 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 block chain, 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 buy 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 totalling 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 principal 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 operation called OP_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 2 second variance).

The two component ledgers of the hybrid interface with 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 block chain 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 (floor(supply/1e6) * 2000) XST, making the maximum number of stakers about 500. For example if the money supply is about 27.5 M XST, then the price for a staker is 54,000 XST (27 * 2000).
  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 increasing with the square root of the number of blocks they have produced, and decrease with the square root of the number of blocks missed. 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. Inflation is 1%, instantaneous, 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 are excellent rates of 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 S * R / (100 * Y), where S is the money supply at the start of the round, R 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. There is no benefit to being first or last in a round.
  12. The order of each round is determined by hash of previous block. To make it difficult to influence ordering, hash of the block immediately preceding a round is hashed using 16 rounds of scrypt to seed a Mersenne twister pseudo random number generator (MT-PRNG) that assigns the ordering by shuffle.
  13. XST owners may vote to disable misbehaving stakers. To remove a staker, at least 33% of XST must vote and of that 33% at least 51% must vote for removal (meaning that the minimum to remove a staker must be 16.83% of all XST). Votes will tallied every 120960 blocks (7 days), automatically. If at least 33% of all XST does not vote, then no stakers are removed. The XST represented by the buy price of a staker is not eligible to vote.

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 this type of auction can have open bidding, it can be conducted entirely on the XST block chain 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

The winning chain shall be decided by a weighted participation score, called 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.

Data Structures

Staker Registry

Stakers will be stored in a registry. Stakers are added to the registry upon creation (purchase from the block chain) and deleted from the registery when they are both terminated and have a 0 balance. To encourage rapid withdrawal upon termination, the balance of terminated stakers will be reduced by 1/10 of the block reward every round. For example, if the block reward for a round is 0.001 XST, then the balances of any terminated stakers will be reduced by 0.0001 XST. Stakers will be assigned the lowest unused identifier. Identifiers can be reused, but must be unique among active stakers.

Each entry in the registry will have the following data structure:

Key: identifier (uint32_t)
Value: Object
    active: bool
    recent_blocks_missed: vector of int
    balance: int64_t
    pubkey: uint256
    blocks_produced_total: int
    blocks_missed_total: int

The active property indicates whether a staker is active or not.

Stakers are created, terminated, and removed from the registry constantly.

The registry allows 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 block signature will have a standard structure of 4 bytes for the staker ID followed by the 70 byte signature.

To significantly reduce unspent outputs, block rewards are added to staker accounts in the staker registry. The minimum claim will be 2 days of block rewards at the reward level and active staker count at the time that the claim is executed. Claims will create new unspent transaction outputs (UTXOs).

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

Staker Queue

Derived from the registry is a simple vector of identifiers of active stakers. This vector is first sorted in ascending order, then shuffled using a deterministic MT-PRNG to obtain the staker queue for a given round. The staker queue is a vector that holds the shuffled stakers for the current round. It is created at the beginning of each round. Along with the queue is a simple index that points to the current staker. This index is 0 at the beginning of a round incremented after each 5 second block period, whether or not a block was produced during that period. A block period 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 calculated, and the index set to 0.

Misbehavior of Stakers

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.

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 block chain. 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 reindex 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 reindex, 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 4 days.

Implementation

To support qPoS, several new opcodes will be added to the scripting language:

  1. Purchase of stakers: OP_PURCHASE
  2. Claim block rewards: OP_CLAIM
  3. Transfer staker: OP_SETSTAKERPUBKEY
  4. Accuse of extraneous production: OP_ACCUSEEXTRANEOUS
  5. Accuse of double production: OP_ACCUSEDOUBE

As part of the output spending script, these opcodes are executed on the Staker Registry when they are spent. For example, the OP_PURCHASE opcode is used to purchase a staker. The structure of the spending script indicates to which pubKey the staker is assigned. This information is added to the Staker Registry when this transaction is spent by the recipient. Note that in this scheme, the introduction of an operation to the blockchain (e.g. OP_PURCHASE) and the execution of the operation to cause the state change occur in two different transactions. This decoupling is a necessity of co-opting the existing scripting engine to process state changes on the Staker Registry.

OP_PURCHASE

Figure 6

https://raw.githubusercontent.com/StealthSend/XSTDocImages/master/qPoS/OP_PURCHASE.png

Summary

  • The pubKey is pushed to the top of the stack, and will be assigned to a new staker.
  • OP_PURCHASE indicates to process the purchase upon spending of the output.
  • If the pubKey is valid, OP_PURCHASE returns nothing to the stack.
  • Only PUBKEY destinations are accepted as an efficiency consideration because the

resulting output is intended to be spent immediately to execute the purchase. * For the Tx of which this output is a part, the total value in must be greater than or equal to the total of all output values, the price of all stakers purchased, and fees.

CScript Template

CScript() << OP_PUBKEY << OP_PURCHASE << OP_PUBKEY << OP_CHECKSIG

Enum Name

TX_PURCHASE

ScriptSig

<sig>

OP_CLAIM

Figure 7

https://raw.githubusercontent.com/StealthSend/XSTDocImages/master/qPoS/OP_CLAIM.png

Summary

  • 0x04 pushes the 4 byte ID onto the stack.
  • OP_CLAIM indicates to claim staker rewards assigned to the staker ID when the transaction spending this output is accepted into the blockchain.
  • OP_CLAIM consumes the ID from the top of the stack.
  • OP_CLAIM returns to the stack the pubKey assigned to the ID when this output is spent.
  • OP_CHECKSIG requires a valid signature for this pubKey, ensuring proper authorization.
  • The claim value must be greater than the minimum claim and equal to or less than the total unclaimed rewards belonging to the staker ID at the time of spending this output, or the unspent output will be considered nonstandard input, causing the transaction to be rejected.
  • The pubKey assigned to the ID at the time of spending receives any value associated with this output.

CScript Template

CScript() << 0x04 << OP_CLAIM << OP_CHECKSIG

Enum Name

TX_CLAIM

ScriptSig

<sig>

OP_SETSTAKERPUBKEY

Figure 8

https://raw.githubusercontent.com/StealthSend/XSTDocImages/master/qPoS/OP_SETSTAKERPUBKEY.png

Summary

  • The pubKey ("new pubKey") that will be assigned to the staker ID will be pushed to the top of the stack.
  • 0x04 pushes the 4 byte ID onto the stack.
  • OP_SETSTAKERPUBKEY indicates to set the ID to the new pubKey if the block with the output is accepted.
  • OP_SETSTAKERPUBKEY consumes both the new pubKey and ID from the top of the stack.
  • OP_SETSTAKERPUBKEY returns to the stack the pubKey ("old pubKey") currently assigned to the ID.
  • OP_CHECKSIG therefore requires a valid signature for the old pubKey, ensuring proper authorization.
  • The old pubKey receives any value associated with this TxOut.

CScript Template

CScript() << OP_PUBKEY << 0x04 << OP_SETSTAKERPUBKEY << OP_CHECKSIG

Enum Name

TX_SETSTAKERPUBKEY

ScriptSig

<sig>

OP_ACCUSEEXTRANEOUS

Figure 9

https://raw.githubusercontent.com/StealthSend/XSTDocImages/master/qPoS/OP_ACCUSEEXTRANEOUS.png

Summary

  • OP_PUSHDATA4 pulls 4 bytes from the input stream, which is the length of data to read.
  • Of this data read, the first 100 bytes are the staker ID and 3 hashes for B1, B2, and B3.
  • Of the remaining, all but the last 4 bytes are chain C1 data.
  • The last 4 bytes indicates the length of C1 data.
  • OP_ACCUSEEXTRANEOUS uses these top 4 bytes to identify the C1 data to create its chain.
  • This operation returns nothing on the stack if the accusation is valid and fails if invalid.
  • C1 blocks are headers only except for blocks B2 and B3, which include signatures.
  • Only PUBKEY destinations are accepted. This is an efficiency consideration because

the resulting output is intended to be spent immediately to execute the accusation.

CScript Template

CScript() << OP_PUSHDATA4 << OP_ACCUSEEXTRANEOUS << OP_PUBKEY << OP_CHECKSIG

Enum Name

TX_ACCUSEEXTRANEOUS

ScriptSig

<sig>

OP_ACCUSEDOUBLE

Figure 10

https://raw.githubusercontent.com/StealthSend/XSTDocImages/master/qPoS/OP_ACCUSEDOUBLE.png

Summary

  • OP_PUSHDATA1 pulls 1 byte from the input stream, which is the length of data to read.
  • This data is always 194 bytes, which is 4 bytes for the staker ID plus 190 bytes of B1B data.
  • B1B data is 120 bytes of header data and the 70 byte offending signature.
  • OP_ACCUSEEDOUBLE creates B1B from data and checks the ID.
  • B1B points to CB, which has as its successor B1A.
  • This operation returns nothing on the stack if the accusation is valid and fails if invalid.
  • Only PUBKEY destinations are accepted. This is an efficiency consideration because

the resulting output is intended to be spent immediately to execute the accusation.

CScript Template

CScript() << OP_PUSHDATA1 << OP_ACCUSEDOUBLE << OP_PUBKEY << OP_CHECKSIG

Enum Name

TX_ACCUSEDOUBLE

ScriptSig

<sig>

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<https://www.akalin.com/computing-isqrt>`_. 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<http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup>`_.

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, 0,
           1,1, 2,2,2,2, 3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4,4, 5,5,5,5,5,5,5,
           5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,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,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,
           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 };

uint8_t bit_length(uint8_t v)
{
    return bit_length_table[v];
}

uint32_t bit_length(uint32_t v)
{
    static const uint32_t ones = -1;
    uint32_t r = 0;
    if (v & (ones << 16)) { v >>= 16; r += 16; }
    if (v & (ones <<  8)) { v >>=  8; r +=  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;
}
You can’t perform that action at this time.