Proof-of-Loss A Purely Symbolic Block-Chaining Algorithm for Monetary Consensus Mirelo Deugh Ausgam Valis This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License (CC BY-SA 4.0). Introduction What is proof-of-loss? It is a consensus algorithm based on a chain of transaction blocks, like Bitcoin or Peercoin. However, it fundamentally differs from either currency, for using lost spending rights to both reward block chaining and determine block-chaining odds. In Bitcoin, each miner sells the conclusion (first confirmation) of transactions for fees. However, those fees could also be buying the right to that conclusion during its occurrence. This valid interpretation is optional since, in Bitcoin, the right to transact is always created and destroyed in the same block. Indeed, although transaction rights are the reward for chaining each block, if only saleable in the same block earning them, they become redundant, hence irrelevant. In proof-of-loss, on the contrary, transaction rights are never created and destroyed in the same block: • They are only saleable in their creating block's descendants. • They must sell before any pending transactions can conclude. Then, for already existing despite not yet saleable, those rights are no longer irrelevant. But what is the right to transact, and how can it sell? If selling requires pricing, which requires valuing, which requires measuring, then how is this right measured? In proof-of-loss, transaction rights are merely the size in bytes of each transaction allowed to conclude: • If that conclusion did not yet happen, then those rights remain. • If it already did, then they have become their loss. However, this loss is not merely the size in bytes of each concluded transaction, but rather that size divided among all spendable outputs from this transaction proportionally to their sizes in bytes. Then: i • For being a representation of the same loss, each of those outputs becomes a "rights output extinction," or a route. • As later specified (see section 3 on page 5), the odds of chaining each block will depend not on the balance of those outputs as in proof-of-stake, but rather on the loss they represent in bytes. • As also later specified (see section 4 on page 7), the reward for chaining each block will be the right to make transactions of a total size combining: ⋄ The loss represented in that block by all spendable outputs from paid transactions. ⋄ The loss represented by the route chaining the same block. Thus, proof-of-loss is a closed or self-referential algorithm, in which only lost rights can (as themselves) reward block chaining or (as their loss) determine block-chaining odds. Even so, transaction volume in bytes can still grow, since the protocol restores those rights both when lost and each time their loss chains a block. So the lifecycle of all spending rights is as follows (the solid lines mean synchrony, and the dotted ones, asynchrony): Transaction Sizes in Bytes OO Transaction Conclusions Represented Losses (in Bytes) //Route Sizes in Bytes Transaction Rights (in Bytes) oo Transaction Conclusions vv Block Chaining Block-Chaining Odds  Likewise, the fraction of each previous reward W for sale in the current block depends exclusively on W and its earning route r, by always having the smaller size in bytes between: • The rights not yet surrendered from W. • The loss represented by r. ii For example, as later confirmed (see section 3 on page 4), if r is the only route of a transaction t, then r's represented loss is t's total size in bytes. Thus: • Let all transactions have the same size in bytes and a single route. • Let the first block have a single transaction. Then, the maximum transaction volume in bytes of the first nine blocks is as follows (with rewards numbered after their source block and losses named after their partly consumed reward): w3 w4 w5 w6 Lossesw1 w2 w3 w4 w5 w6 w7 w0 w1 w2 w3 w4 w5 w6 w7 w8 b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 Blocks w1 w1 w2 w3 w3 w4 w5 w6 w6 Rewards w2 w3 w4 w5 w6 w7 w4 w7 w5 w6 w8 w7 w8 w9 Where: 1. The route r0 chaining the first block b1 earns the reward w1, of which the size in bytes restores the rights consumed by the single transaction t1 (spending r0) in b1 plus those having their loss l0 (at r0's funding transaction t0 in the dummy block b0) represented by r0: iii • Again as later specified (see section 8 on page 14), t1's input instructs the system to redistribute r0's collected fees to t1's route r1. • As later confirmed (see section 4 on page 7), w1's fraction for sale in the second block b2 has the size in bytes of l0, so b2 has a single transaction t2 of the size in bytes of t1. • Since no longer spendable, r0 cannot keep representing l0, nor hence chain any blocks after b1. 2. Then, t1's route r1 chains b2, thus earning the second reward w2, of which the size in bytes restores w1's fraction consumed by the single transaction t2 (spending r1) in b2 plus the loss l1 (at t1) represented by r1: • t2's input instructs the system to redistribute r1's collected fees to t2's route r2. • The third block b3 puts for sale equal parts of w1 and w2. 3. For chaining b3, r2 earns the third reward w3, of which the size in bytes restores the rights partly consumed from w1 and w2 by the now two transactions t3 and t4 in b3 plus the loss l2 (at t2) represented by r2: • t3 spends r2, then t4 funds its route r4 with t3's one r3. • t3's input instructs the system to redistribute r2's collected fees to r3, then t4's one, to redistribute them to r4. 4. For chaining the fourth block b4, r4 earns the fourth reward w4, of which the size in bytes restores the rights partly consumed from w2 and w3 by again two transactions t5 and t6 in b4 plus the loss l4 (at t4) represented by r4: • t5 spends r4, then t6 funds its route r6 with t5's one r5. • t5's input instructs the system to redistribute r4's collected fees to r5, then t6's one, to redistribute them to r6. 5. And so on. This way, the maximum transaction volume in bytes of the first 90 blocks is as follows: iv Losses Blocks Despite seemingly unrealistic, this example perfectly describes a currency bootstrap scenario. After the bootstrap, transaction volume in bytes can grow faster or slower or even shrink since: • A block can contain any number of paid transactions, each with a different size in bytes and number of routes or inputs. • As later specified, rewards failing to sell are partly revocable (see section 4 on page 8) and inheritable (still section 4 on page 8). • As also later specified (see section 7 on page 13), people can prevent the currently saleable fraction of any - overpriced - rewards from selling in their chained blocks. The use of transaction rights makes proof-of-loss purely symbolic, meaning the algorithm need not rely on losses external to the block chain, as proof-of-stake and -work do, but merely on the symbols representing those losses. Instead of relying on amassed stake or hashing power to (unprovenly) represent lost wealth, proof-of-loss relies on the size in bytes of concluded transactions to (provenly) represent lost spending rights. This purely symbolic design logically evolves into systemic policies, like inactivity fees (on page 23), intrinsic checkpoints (on page 26), and an adaptive monetary policy (on page 27). But not before also naturally addressing the problems: • Of a lacking organic block size limit, by making transaction volume in bytes both economically prioritized (see page 11) and dependent on its past. • Of "nothing at stake," by making the child of a block provenly chained in parallel inherit both all earned rights and seized fees in its parent (see page 19). v Proof-of-Loss Mirelo Deugh Ausgam Valis cba 1 Block-Chaining Incentives The coinbase parameter of Bitcoin's genesis block reads: The Times 03/Jan/2009 Chancellor on brink of second bailout for banks This reference indicates Bitcoin was designed to at least partly prevent a future global monetary crisis like that of 2008. However, any such crisis is only the ultimate result of money falsely becoming its represented wealth,1 hence of a confusion Bitcoin could never entirely avoid as its proof-of-work incentive model: 1. Makes hashing-power create or collect its monetary reward, of which it cannot produce the represented wealth. 2. Requires those earnings to be worth more than paid for that power, so by self-expanding, money becomes indistinguishable from the wealth thus additionally represented. Eventually, by eliminating hashing-power mediation, proof-of-stake would reduce this design problem to its essence. For example, in Peercoin,2 the reward for chaining each block depends directly on the size and age of the stake enabling that chaining. This model can appear to be fair for keeping all its rewards invariable in relative size, hence by making them purely relative. However, these rewards could only have such an irrelevant absolute size by being market prices, which must rather be variable also in relative size. Thus, if still incompletely variable, the same rewards must eventually cause recursive increases in wealth inequality. For example, Peercoin tends to reward people with a newly created or minted 1% of their block-chaining stake per year. So: • Let Peercoin be the only form of money for both Bob and Alice. 1 This fact would not prevent some people from likening Bitcoin's monetary system to a "decentralized autonomous company" (DAC) as if money could indeed produce its represented wealth. 2 Also known as Peer-to-Peer Coin or PPCoin, both sharing the acronym PPC, Peercoin was the first block-chained monetary system to use proof-of-stake. Page 1 of 76 Block-Chaining Incentives Proof-of-Loss Mirelo Deugh Ausgam Valis cba • Let Bob's income be 100 monetary units or coins per month while his expenses are 80% of his income. • Let Alice's income be 400 coins per month while her expenses are 50% of her income. • For simplicity, neither let Bob nor Alice have any savings - which Alice is more likely to have. Then, Bob and Alice will be able to stake 20 and 200 coins per month, respectively, so most likely: 1. Alice's minted reward will exceed Bob's by 900%, even though her income exceeds his by only 300%. 2. In addition to earning an undue proportion of their combined mintage, Alice will be able to stake this excess reward in the future, thus increasing it exponentially. The only way to prevent the resulting price inflation from causing an increasingly uneconomical wealth transfer from Bob to Alice is letting a market decide the block-chaining reward, so proof-of-stake stops raising the following questions: 1. How much newly created money must people be able to earn as their incentive to engage in block chaining? Is 1% of stake per year enough? If not, then what about 6%? 2. How can the chosen percentage neither underestimate people's greed nor overestimate their willingness to fund uneconomical, undue rewards through the resulting price inflation? Still, unless each block-chaining reward consists exclusively of transaction fees,3 no market could remain its only determinant since prices are everything a market can determine completely. Hence, only markets for the product costing those fees can economically decide that reward, yet what could people exchange in these markets? What have transaction fees ever priced? 3 In Bitcoin, although transaction fees were always part of the block-chaining reward, they will not be all of it until mining yields become less than the current coin precision, by halving each four years from 50 coins in 2009. Page 2 of 76 Block-Chaining Incentives Proof-of-Loss Mirelo Deugh Ausgam Valis cba 2 Transaction Rights The essential purpose of chaining blocks is neither to create nor even to earn additional money, but only to conclude (first-confirm) transactions. However, to optimize monetary consensus, people must always have an incentive to engage in block chaining, even while having no need to transact. Hence, as concluding their unowned transactions cannot reward them: 1. These people must have an incentive to chain blocks other than satisfying anyone's need for monetary transfers. 2. To never let extrinsic reasons make it fail, this additional incentive must remain as essential a purpose to block chaining as concluding those transfers. Indeed, all spenders whose transactions depend on block chaining have already an impermanent incentive to chain blocks. Then, how to make that incentive permanent to each one of them? Only by requiring each transaction conclusion to be sold, so those offering it can earn at least what they would pay for a similar offer. Still, the mere possibility of a transaction conclusion is never saleable. Thus, if that conclusion must sell before it can occur, then how could it sell? Only as a right: the right to the same conclusion, or to transact.4 So people chaining blocks must always be rewarded with transaction rights instead of transaction fees, and only later sell those rights for those fees. 4 Except as a right, each transaction conclusion must become indistinguishable from its product, since only saleable as a concluded transaction - as in Bitcoin - rather than as merely the right to transact. Page 3 of 76 Transaction Rights Proof-of-Loss Mirelo Deugh Ausgam Valis cba 3 Proof-of-Loss However, despite transaction rights being the only block-chaining reward left, amassing stake or hashing-power does not require selling those rights. Instead, it can result from selling actual wealth - that with purely material utilities. Hence, if amassed stake or hashing-power can still determine block-chaining rewards, even by only affecting block-chaining odds, all money thus earned remains indistinguishable from what it can buy. For that money to ever be distinguishable from its represented wealth, block-chaining odds must depend instead on used hence lost transaction rights, which are then recoverable either by chaining blocks or paying others for doing so, regardless of amassed stake or hashing-power. Indeed, money cannot remain decentralized unless each of its users either keeps it spendable or pays others for doing so, regardless of this user's accumulated wealth. While conversely, since transaction rights (hence block chaining) depend on money being spendable, any lost such rights (hence the resulting block-chaining odds) can only take the form of spendable outputs, each of which is then a route, meaning a "rights output extinction" r, as follows: lr = zt × zr Zr Where: • lr means r's represented loss in bytes. • zt means the total size in bytes of r's transaction t. • zr means r's total size in bytes. • Zr means the combined size in bytes of all of t's routes, whether spent or not - including r. Indeed, only the size in bytes of each concluded transaction is a purely symbolic block-chaining resource, by representing nothing external to the public chain of blocks, including any wealth - which must remain both external to that chain and optionally private. Still, this protocol must always: Page 4 of 76 Proof-of-Loss Proof-of-Loss Mirelo Deugh Ausgam Valis cba • Minimize the block-chaining odds of each recently rewarded or concluded route r, so its owners (who can have concluded it) are unlikely to monopolize the block-chaining process. • Reduce these odds proportionally to the rights not surrendered from those earned in r's most recently chained or else concluding block, so r's owners (who can have concluded it) are unlikely to monopolize the existing transaction rights. Additionally, the same protocol must always: 1. Reduce r's block-chaining odds proportionally to its block depth, so people are unlikely to monopolize future block chaining by accumulating spendable outputs. 2. Increase these odds proportionally to the already surrendered rights from those earned in r's most recently chained or else concluding block, so any depthless routes consuming those rights are unlikely to monopolize the block-chaining process. Then, r's block-chaining odds must be: xr = xg × lr × ( lr Wr + (( 1− lr Wr ) × Ws Wr )) × Ws lr + 1 dr Where: • xr means r's difficulty target, which is inversely proportional to r's block-chaining difficulty. • xg means the general difficulty target, which must self-readjust to maintain a constant block interval. • lr means r's represented loss in bytes (as already specified on the preceding page). • Wr means the total earned rights in r's highest chained or else concluding block. • Ws means the total surrendered rights from Wr. Page 5 of 76 Proof-of-Loss Proof-of-Loss Mirelo Deugh Ausgam Valis cba • dr means r's block depth, which is always the depth of r's concluding rather than highest chained block.5 This use of transaction rights as the only block-chaining reward and of their loss as the only factor of block-chaining odds utterly defines the proof-of-loss protocol. Further specifying this protocol must never make that definition invalid. So proof-of-loss alone can distinguish any block-chained money from its represented wealth, for being the only block-chaining monetary consensus algorithm ever to prove the loss of something purely symbolic, which is everything always distinguishable from any wealth. For the same reason, although every algorithm for block-chained monetary consensus must assume a proof of loss, only proof-of-loss can provide it.6 Indeed: 1. No other such algorithm can directly represent what it must prove was lost - nor then be purely symbolic. For example, amassed stake or hashing-power could never be the lost wealth that respectively proof-of-stake or -work must prove. 2. Every loss represented by something other than itself must be uncertain - since not purely symbolic. For example, in proof-of-stake or -work, the costs respectively of amassing stake or hashing-power can always be overvalued. However, nothing could be uncertain to whom it was proven. So, in a block-chained monetary system, lost spending rights (represented by the size in bytes of each concluded transaction divided among its routes proportionally to their sizes in bytes) are the only possible form of proof-of-loss. 5 Unlike a block-chaining reward in money, one in rights need not (nor otherwise could) add to its earning balance by transferring it. 6 Knowingly or not, all other block-chaining consensus algorithms, regardless of their proof of choice, intend to be forms of proof-of-loss. However, they could never prove any loss. Instead, they can only prove something that indicates one. Page 6 of 76 Proof-of-Loss Proof-of-Loss Mirelo Deugh Ausgam Valis cba 4 Block-Chaining Rewards To prevent money from falsely becoming its represented wealth, not only the odds of chaining a block but also the size in bytes of the resulting reward must depend entirely on lost rights. Otherwise, these rights could become that reward even without being lost, thus letting the money buying their loss create more of them - so their selling would make that money again self-expand. Hence, the same reward can only be the paid rights: • Of which the loss enables the block chaining thus rewarded. • Lost on all transactions buying rights in the chained block.7 Additionally, as either possibility alone would prevent transaction volume in bytes from ever increasing, the reward for chaining each block must always combine: • The paid part of that volume concluded in this block. • The size in bytes of the loss chaining the same block. Still, a route must not be able to sell its earned rights in its chained blocks, or the resulting losses would become its additional rights, which could recursively become ever more such losses. Likewise, if any rights currently for sale exceeded their earning loss, then their market share would not be proportional to that loss, nor hence the resulting price competition to block-chaining decentralization. Thus, each reward's fraction for sale in a subsequent block must always be the smaller in bytes between that reward's rights not yet surrendered and the loss their earning route represents. However, this policy must also: • Prevent total existing rights from increasing unnecessarily, which could make their price fail as a block-chaining incentive. • Allow them to decrease at most by their possible increase, to avoid unduly prioritizing either variation. 7 So the size in bytes of all block-chaining rewards must exclude that of any transactions originated by the system. Page 7 of 76 Block-Chaining Rewards Proof-of-Loss Mirelo Deugh Ausgam Valis cba So, since each block-chaining reward W can later increase total sold rights by at most its earning loss l, the rights from W that fail to sell must be revoked at most by l × 2, as follows: Kc =          0 if c 6 u Sc − Bc if c > u and Sc − Bc 6 2l −∑Kh < c 2l −∑Kh < c if c > u and Sc − Bc > 2l −∑Kh < c Where: • Kc means W's rights revoked at the current block height c. • u means l's chained block's height, at which W is unsaleable.8 • Sc (6 l) means W's fraction for sale at c. • Bc means Sc 's rights bought at c. • Kh < c means W's rights revoked at each block height h lower than c. So, for c = u + 3: ∑Kh < c = Ku + 1 + Ku + 2 Finally, to minimize the selling opportunities of any overpriced rights while maximizing their opportunities to sell at a lower price, the reward for chaining a block b must inherit any unsold rights failing to sell in b's parent p but not revoked, as follows: Up = Sp − ( Bp + Kp ) Where: • Up means the unsold rights inherited by b's chaining reward from p's one. • Sp means the rights for sale in p, whether inherited or not by the reward for chaining b or p from that for chaining their parents. • Bp means Sp 's fraction bought in p. • Kp means Sp 's fraction revoked in p. 8 As already specified on the previous page, all transaction rights must be unsaleable in the same block of which they reward the chaining. Page 8 of 76 Block-Chaining Rewards Proof-of-Loss Mirelo Deugh Ausgam Valis cba 5 Price Negotiation In Bitcoin, for not being formally asked, the price miners charge for transaction rights must remain implicit in paid transaction fees, which hence: • Can only be an estimate of this informally charged price. • Can only be an implicit bid since one on informal asks. While conversely: • When spenders overvalue a transaction conclusion - as they are more likely to do the less they can wait for it - miners cannot price it lower. Then, transaction costs uneconomically rise. • When spenders undervalue that conclusion - as they are more likely to do the more they can wait for it - miners cannot price it higher. Then, transaction costs uneconomically fall. So, for the same conclusion always to be priced economically: • Selling it requires formalizing its asked price, by publicizing transaction asks. • Buying it requires formalizing its bid price, by converting every paid transaction fee from an implicit ask estimate into an explicit ask payment. Indeed, only this way people must always negotiate the right to transact in the market, since: • To maximize their gains, they must make no asks above a maximum bid. • To minimize their wait for transaction conclusions, they must make no bids below a minimum ask. Page 9 of 76 Price Negotiation Proof-of-Loss Mirelo Deugh Ausgam Valis cba 6 Block Chaining So each paid transaction must inform not the asked fees it pays but rather its maximum payable ones, or its formal bid on its price. Then, for each block b to contain a valid combination of bids and asks, its total asked fees need only not exceed its total bid ones. Hence, b's every ask k must collect in b the following fees: fb = Fb × fz Fz Where: • fb means k's total earned transaction fees in b. • Fb means the total bid such fees in b, as follows: Fb = ( ft1 × zt1 ) + ( ft2 × zt2 ) + * * *+ ( ftn × ztn ) Where: ⋄ ft1 , ft2 , . . . , ftn mean the maximum payable fee per byte for each of b's concluded transactions t1, t2, . . . , tn. ⋄ zt1 , zt2 , . . . , ztn mean the total sizes in bytes of t1, t2, . . . , tn. • fz means k's total charged fees in b, as follows: fz = fk × zb Where: ⋄ fk means k's charged fee per byte of transaction rights. ⋄ zb means the size of k's total sold rights in b, as follows: zb = zk Zk × Zt Where: Page 10 of 76 Block Chaining Proof-of-Loss Mirelo Deugh Ausgam Valis cba ◆ zk means the total size of k's rights for sale in b. ◆ Zk means the size of the combined rights for sale in b by all of b's asks - including k. ◆ Zt (6 Zk) means the combined size in bytes of all transactions buying rights in b. • Fz (6 Fb) means the total fees charged in b, as follows: Fz = fz1 + fz2 + * * *+ fzn Where fz1 , fz2 , . . . , fzn mean the same as fz on the preceding page but each calculated for one of b's asks k1, k2, . . . , kn - again including k. However: 1. Older transaction inputs must have priority over newer ones, or a spend could wait endlessly for its conclusion. 2. Smaller independently concluded losses must take precedence over larger ones, or it would still be possible to monopolize block chaining by displacing other people's losses with corresponding ones represented in fewer transactions by spendable outputs belonging to the monopolists. Thus, to provide a combined incentive to these two required forms of transaction prioritization, b's chaining route r must seize the following fees from Fb: fr =    fb ×Qb if fb × Qb 6 Fb Fb otherwise Where: • fr means r's collected fees in b. • fb means the median fb. 9 9 A median is the center of an ordered list k of possibly repeated values. If the number of those values is odd, then their median is the single value at the center of k. Otherwise, it is the average of k's two intermediate values. For example: • With k = {1, 1, 1, 3, 4}, the median of its values is 1. • With k = {1, 1, 1, 3, 4, 200}, the median of its values is (1 + 3)÷ 2 = 2. Page 11 of 76 Block Chaining Proof-of-Loss Mirelo Deugh Ausgam Valis cba • Qb means the prioritization quotient, as follows: Qb = di di × zt zt Where: ⋄ di means the average block depth of all inputs to transactions buying rights in b.10 ⋄ di means the median such depth. ⋄ zt means the median size in bytes of those transactions. ⋄ zt means the average size in bytes of the same transactions. This algorithm provides the following benefits: • By requiring bids and asks in each block to collectively rather than individually combine: ⋄ It maximizes transaction volume in bytes, for letting some of those bids or asks conclude respectively below and above any of their asked or bid prices. ⋄ It maximizes the downward pressure on fees, for making higher bids pay for lower ones. • It enforces transaction prioritization with economic incentives, which are then intrinsic rather than (as in Bitcoin) extrinsic to the protocol. 10 The average of any values is their sum divided by their number. For example, if an ordered list k consists of possibly repeated values, then: • With k = {1, 1, 1, 3, 4}, the average of its values is 10÷ 5 = 2. • With k = {1, 1, 1, 3, 4, 200}, the average of its values is 210÷ 6 = 35. Page 12 of 76 Block Chaining Proof-of-Loss Mirelo Deugh Ausgam Valis cba 7 Transaction Asks Each transaction ask must be a section of the block in which it earned its priced rights. Otherwise, those rights would tend to be overpriced, as they could be repriced lower if necessary. Still: • Intentionally or not, people can always overprice their earned rights, even without being able to reprice them lower. • A falling transaction volume in bytes can also make the same rights eventually overpriced. Then, some transaction rights can become unsaleable. So, to avoid still requiring any blocks to sell them, every block must inform a (possibly empty) list of block depths corresponding to its excluded transaction asks. Otherwise, those unsaleable rights could: • Stop the system. • Slow block chaining, thus increasing the block interval. • Weaken the competition between transaction-right sellers, thus reducing its lowering pressure on fees. However, why not just prevent enough overpriced rights from selling, instead of excluding their asks? Because, as a fractional bid cannot affect which asks its block includes, neither could a fraction of an ask's rights for sale in that block do so, or fees would tend to rise. Finally, as the resulting absence of any asks only makes their saleable rights formally overpriced, any rights then prevented from selling must still follow the same rules both of revocation (on page 8) and inheritance (on page 8) as if rather for sale. Page 13 of 76 Transaction Asks Proof-of-Loss Mirelo Deugh Ausgam Valis cba 8 Implicit Transactions Then, as the source of all fees paid to a rewarded route in each block is any of this block's bids, being thus indeterminate unless the same block has a single bid or ask: 1. This payment must be an implicit transaction transferring those fees to that route. 2. The total output balance of each paid transaction must equal its total input balance less its informed bid. While conversely, spending a rewarded route before it surrendered all its earned rights must have: • All its future collected fees implicitly redistributed as instructed by the transaction input spending it, to prevent the unduly destruction of money. • All its currently collected fees also implicitly redistributed as thus instructed, to prevent any of those fees from paying it with part of its balance. Page 14 of 76 Implicit Transactions Proof-of-Loss Mirelo Deugh Ausgam Valis cba 9 Block Asks As merely finding a proof of loss requires no block chaining, but rather just route ownership, after doing so people can immediately broadcast a block ask informing: 1. The primary proof-of-loss data l capable of becoming that proof by having its difficulty-matching hash signed,11 containing the following items: 1.1. The unique identifier of a route r. 1.2. The present moment,12 so each moment requires hashing another proof of loss. 1.3. The current block height, so blocks at different heights cannot contain the same proof of loss. 1.4. The current difficulty target for the route with the oldest not surrendered rights. The only function of this item is to minimize block reand pre-chaining odds, by providing each proof of loss with a strongly correlated context (which results from all dependencies already specified on pages 5 and 8).13 2. A hash of l not above r's difficulty target, to minimize the odds of multiple routes chaining blocks at the same block height. 3. A signature of this hash by the private key to r's address, to authenticate r's proof of loss. Then, this block ask's broadcasters can also publicize their network location as a bids verifiable destination d, so: • Each unconfirmed transaction can be sent directly to d rather than unnecessarily broadcast. • These people can later publicize another such location ask, as needed if: ⋄ They relocate logically, whether also physically or not. 11 Such as a Peercoin kernel can prove a stake - thus indicating lost wealth - by having its difficulty-matching hash signed. 12 Possibly the current second as in Peercoin. 13 This context has the same function as Peercoin's less organic stake modifier. Page 15 of 76 Block Asks Proof-of-Loss Mirelo Deugh Ausgam Valis cba ⋄ They broadcast another block ask. Indeed, to function as a bids verifiable destination, each advertised location ask must inform: 1. A list of network (possibly IP) addresses, all of which can belong to the advertiser's peers who know its location. 2. The proof-of-loss hash L identifying a block ask b. 3. The serial number of this reference to b's proof of loss, to let people determine its current validity. 4. A difficulty-free hash N of this location ask c. 5. A signature of N by the same private key signing L in b, to authenticate c. This algorithm provides the following benefits: • To minimize memory consumption and even the complexity of selecting transaction destinations, people can discard any block asks informing a present moment not later than the current block's creation time, as also the corresponding location asks. • To prevent memory overflow attacks despite always allowing alternative destinations to propagate, people receiving different location asks for the same proof of loss can relay that with the greatest unrepeated serial number, then discard the others. • To counter denial of service attacks on the network addresses informed by current location asks, people can still: ⋄ Broadcast each unconfirmed transaction otherwise destined only to those addresses (the same ones possibly locating neither their advertiser nor part of its peers). ⋄ Use any addresses not being attacked to receive other people's broadcast candidate spends. Page 16 of 76 Block Asks Proof-of-Loss Mirelo Deugh Ausgam Valis cba 10 Transaction Forwarding However, people broadcasting new location asks will often receive excess pending transactions. Fortunately, they later will have all incentives to forward each still pending one to another such broadcaster, as not doing so: • Could reduce their future sold rights, by lessening the demand for transaction conclusions. • Could reduce their future block-chaining rewards, by decreasing transaction volume in bytes. • Could reduce their money's future value, by extending the wait for transaction conclusions. Then, the only motivation for a location asks broadcaster to neither conclude nor forward a pending received transaction t would be to disrupt the system. However: • There can be additional such broadcasters holding t, who most likely would still choose to either conclude or forward it. • People can always: ⋄ Resend t to the broadcasters of new location asks, and even probe them for t before doing so. ⋄ Broadcast t. Page 17 of 76 Transaction Forwarding Proof-of-Loss Mirelo Deugh Ausgam Valis cba 11 Optional Centralization Always to optimize monetary consensus, people unable or unwilling to engage in block chaining must be able to sell their loss to those then willing and independently able to do so. For example, a route r with a multi-signature address N must be able to lease its represented loss l to a rather single-signature address route, thus making it easier or even possible to sign a proof p of l. Indeed, the block eventually containing p can also include a block-chaining authorization informing: 1. The price of leasing l as a fraction of r's total earned fees. 2. The unique identifier of a second route d to receive an implicit transfer of the remainder. 3. This authorization's hash signed by the required number of N's private keys. Then, only d's owners can use r to chain this block, by signing p with the private key to d's address. While conversely, from its total earned fees in each subsequent block, r must collect only the fraction constituting its lease price, and d only the remainder. This algorithm provides the following benefits: • It tends to minimize the proportion of spendable outputs not engaged in block chaining, or idle. For example, it allows charging for a multi-signature wallet W by requiring W's routes to be leased at most for the remainder of W's price. • It lets all money owners control the degree and profitability of block-chaining centralization, by deciding: ⋄ Whether or not to engage in block chaining. ⋄ Whether or not to lease their eventually idle routes, to whom, and for which price. • It lets people prevent any future leasing of their spendable outputs merely by remitting them to themselves. For example, it allows W's users to transfer each route for lease in W to another (singleor multi-signature) wallet they also control. Page 18 of 76 Optional Centralization Proof-of-Loss Mirelo Deugh Ausgam Valis cba 12 Forcibly Serial Chaining Still, to increase their block-chaining odds or even disrupt the system, people could publicize different blocks chained by the same route at the same height,14 thus arbitrarily delaying the consensual selection of a single chain. To discourage this parallel chaining: 1. The header H of each block b: 1.1. Must contain a section including: 1.1.1. The primary proof-of-loss data (see item 1. on page 15) for a route r. 1.1.2. b's transaction ask (see page 13) and transaction-ask exclusion list (again on page 13). 1.1.3. The hash of b's parent. 1.1.4. The hash-tree root T of all (explicit) transactions in b. 1.1.5. r's optional block-chaining authorization (see page 18), along with all of b's remaining data not affecting T. 1.2. Can include another block's header D. 1.3. Must contain b's authentication, consisting of H's - which is then also b's - hash signed by the private key to either r's or r's authorized route's address. 2. The mandatory section of D: 2.1. Must have the same block height as b's parent p. 2.2. Must reward the same route as p does. 3. D must differ from p's header. 4. If H includes D, then r must inherit p's total chaining reward and collect all seized fees in p (see page 11). This way, to avoid having all their latest earnings in both rights and fees inherited by others, people will always tend to engage in serial rather than parallel block chaining. 14 A similar vulnerability affects proof-of-stake, which - unlike proof-of-work - also needs "nothing at stake" of people's actual "wealth" (hashing-power). Page 19 of 76 Forcibly Serial Chaining Proof-of-Loss Mirelo Deugh Ausgam Valis cba 13 Block Interval Each proof of loss must inform a moment: • Not later than the present one, or people could chain blocks in the future. • Later than the creation time of its eventually containing block's parent, or people could chain blocks in the past. However, people can always delay broadcasting a block, for example, to increase their block-chaining reward. Then, to prevent this delay from slowing the system by reducing total proof-of-loss possible moments hence total block-chaining odds: 1. The creation time of all blocks must be that of - the only moment informed by - their contained proof of loss, which it could not antecede. 2. The block interval must be the delay between the creation rather than broadcast times of any two consecutive blocks. This algorithm provides the following benefits: • It tends to prevent block-interval manipulation, by making each block's creation time depend entirely on block-chaining odds.15 • It tends to minimize chain-fork frequency and competing-branch length while maximizing block-interval resilience, by making total proof-of-loss possible moments hence total block-chaining odds proportional simultaneously: ⋄ To any delays in block broadcast or propagation. ⋄ To the age of the oldest current block. 15 Then, as the (purely block-interval adjusted) general difficulty target will also depend entirely on the same block-chaining odds, the proof-of-loss context (see item 1.4. on page 15) becomes even harder to manipulate. Page 20 of 76 Block Interval Proof-of-Loss Mirelo Deugh Ausgam Valis cba 14 General Difficulty Target However, to still minimize the block-chaining odds of each recently rewarded or concluded route (as already specified on page 5), the longest protocol reduction to those odds must become a proportional discount applied to the current block's deviation from its target creation time. Otherwise, if expanding, this block-interval variation would tend to restore some of those protocol-reduced odds by raising the general difficulty target, which at the current block height h (> 0) must hence be: xh =               xh− 1 ×      1 + ib Ib − 1 dw      if h > 1 xg otherwise Where: • xh means the general difficulty target at h. • xh− 1 means the general difficulty target at h− 1. • ib means the actual interval between blocks at h− 1 and h− 2. • Ib means the target interval between any two consecutive blocks. • dw means the block depth of the oldest reward not entirely surrendered by the route that earned it. • xg means the first general difficulty target. This algorithm provides the following benefits: • It tends to discount all deviations from the block target interval (that affect the general difficulty target) proportionally to the systemic number of routes, hence to the resilience of systemic block-chaining odds. • It tends to exclude all privately chained branches from the longest chain proportionally to the rate of lost rights publicly engaged in block chaining, hence to system usage. Page 21 of 76 General Difficulty Target Proof-of-Loss Mirelo Deugh Ausgam Valis cba 15 Consensual Chain Selection Since proof-of-work or -stake must prove a loss they can just indicate, in both of them: 1. Indication becomes indistinguishable from proving. 2. As much as an event, each proof of an indicative sign must already be that sign, as which it must also prove what the same sign - then its proof - indicates. Hence the need for Bitcoin's consensually selected chain to represent not only the most proof-of-work events but also the most work, and for Peercoin's one to represent not only the most proof-of-stake events but also the most stake-age destroyed, as if this way both could prove the resulting economic losses. While conversely, since unlike any such indirectly representational consensus algorithms, proof-of-loss needs no additional proof of loss, its consensually selected chain only needs to contain the most proof-of-loss events (the most blocks) during the same time by always targeting the same block interval,16 being thus merely the longest chain. 16 So, if any of its branches differ in their block-interval target at the same height, then their number of blocks is no longer comparable unless multiplied in each of them by the average such target for that branch. Page 22 of 76 Consensual Chain Selection Proof-of-Loss Mirelo Deugh Ausgam Valis cba 16 Inactivity Fees By chaining blocks, people are bearing the costs of maintaining the system. Hence the need for transaction fees. Indeed, since people transferring money are actively benefiting from that system, it is only fair that they refund its maintenance costs. However, money owners not making transactions are also benefiting from the same system, despite rather passively. Thus, it is equally fair that they also refund its maintenance costs, but how? If a spendable output remains unspent after the loss following it has reached all that currently represented, then its faster-spent companions will alone have partly refunded the maintenance costs of all routes not spent since its block height, so: • It no longer bears its maintenance costs. • It becomes inactive. Then, this route must no longer be spendable unless by paying additional fees. Otherwise, it could avoid refunding any fees paid to keep it spendable during its inactivity. Still, how much more money must people pay to spend an inactive route r? Each time the loss either following r or the last such time reaches all that currently represented, r's missing transaction's bid must add to its total owed fees. Hence, to minimize price manipulation, this repeated increase in r's owed inactivity fees must be: ur = ft × lr Where: • ur means each undivided increase in r's owed maintenance costs. • ft means the median bid transaction fee per byte in r's last indivisible inactivity period.17 • lr means r's represented loss (as already specified on page 4). 17 A median (see note 9 on page 11) is the most resistant statistic, although its resistance remains proportional to its data sample, hence to the number of transaction-fee bids concluded in that period. Page 23 of 76 Inactivity Fees Proof-of-Loss Mirelo Deugh Ausgam Valis cba Finally, for consisting only of sufficiently deferred transaction fees, all inactivity fees must be both paid and collected as if still buying the right to transact. Hence, they must always be: • Paid by a fraction of their incurring transaction's bid, so this bid cannot be less than the combined inactivity fees owed by all inputs to its containing transaction. • Collected by all the current block's asks as paying any otherwise unpaid part of their charged fees. This algorithm provides the following benefits: • Even if any balances or transaction histories are secret,18 it causes all irretrievable balances (all unspent "dust") eventually to become publicly spent, then subject to pruning if holding no rights. Indeed, regardless of balances or transaction histories, a route r concluded at a block height h will become publicly spent once all fees currently owed by all routes at h (including r) plus all since paid by those excluding r correspond at least to h's total money supply plus all implicit earnings by r. • Spending a long-unspent output becomes less likely to cause a subsequent spike in money velocity, as the total inactivity fees paid by this route: ⋄ Will have increased proportionally to its total inactivity. ⋄ Will disperse among all the current block's asks by being part of each one's collected bids. • Trying to increase block-chaining odds by accruing spendable outputs will cost proportionally to the resulting gains, as the combined inactivity fees owed by all thus (im-)mobilized routes will keep often increasing. • Each pending transaction will eventually expire, once all its owed inactivity fees have exceeded its bid. 18 In proof-of-loss as in proof-of-stake, block-chaining decentralization requires a minimum rate and integrity of spendable-output private - hence optionally secret - ownership. However, unlike in proof-of-stake, in proof-of-loss, this route-possession privacy can always benefit from optionally secret balances (which in proof-of-stake exclude stakes) and transaction histories. Page 24 of 76 Inactivity Fees Proof-of-Loss Mirelo Deugh Ausgam Valis cba • To at least regain their paid inactivity fees if not earn additional ones, people will be more likely to engage in block chaining. • To increase their competitiveness as transaction-right sellers, people obtaining unusually large gains from inactivity fees will tend to reduce - although by less than those excess earnings - their asked price for the right to transact. • To reduce their paid inactivity fees, people will tend to: 1. Unify their unspent outputs, thus minimizing the required size in bytes of the longest chain, by trying to: ⋄ Consolidate their incoming transactions - for example, with payment channels. ⋄ Avoid fragmenting their balance for other purposes than spending it. ⋄ Combine their already fragmented earnings that tend to remain unspent, thus reducing the number of transaction outputs not subject to pruning. 2. Lend their otherwise unused money, thus letting other people spend it productively. Page 25 of 76 Inactivity Fees Proof-of-Loss Mirelo Deugh Ausgam Valis cba 17 Intrinsic Checkpoints Chaining a block is voting on its parent as belonging to the longest chain. Thus, if the combined loss L represented by all spendable outputs at a block height h no longer exceeds that chaining all descendants of a block b at h, then L already had the opportunity to vote or not on b, so b's descendants: 1. Must represent L's vote on b as belonging to the longest chain. 2. Must make b unalterable, or a checkpoint, since L's vote is that of all spendable outputs at h. This algorithm provides the following benefits: • It tends to keep the chain section above the current checkpoint as lengthy as needed, for making its length proportional both directly to the total represented loss immediately below it and inversely to the average loss represented by all its rewarded routes, hence directly to block-chaining decentralization. • For a privately chained branch B to then replace its competing section of the consensually selected chain C, people chaining B must either: 1. Keep it above the current checkpoint until published, when it will hence most likely still be shorter than that section. 2. Target only people at least partly unaware of C, by providing them with a B that: ⋄ Includes as many transactions from C as possible, for these people to most likely accept B. ⋄ Lacks each transaction t from C that B's additional ones must expire by deferring (see page 24), along with any spends directly or indirectly depending on t. So B will tend to be rejected. • It allows pruning the chain section simultaneously below the current checkpoint and the oldest routes with any rights, money, or affecting monetary policy or the proof-of-loss context. • It allows each protocol change (like a younger first block or a different block target interval) to depend on most blocks above the current checkpoint voting in its favor. Page 26 of 76 Intrinsic Checkpoints Proof-of-Loss Mirelo Deugh Ausgam Valis cba 18 Adaptive Monetary Policy Not even a decentralized monetary system can operate without a monetary policy, which hence must be part of its design. Indeed, there is no decentralized way of defining general rules for creating or destroying money other than recognizing their necessity. Then, a decentralized monetary policy must result from its justification, which in proof-of-loss is as follows: 1. For prices to remain stable, the money supply must always change proportionally to its demand, thus independently of all transaction outputs no longer spendable, which cannot represent people's current demand for money. 2. Only the number of routes could represent how much money people currently need since: • Monetary balances cannot create that demand, for already being the actual money supply. • Lost rights cannot do it either, for only demanding enough money to pay their price in transaction or inactivity fees. • The number of spendable outputs at each block height must be at least that of their independent owners, who are collectively the source of all demand for money. • Eventually paid inactivity fees will always provide enough incentive to minimize the number of spendable outputs (as already specified on page 25). 3. Only still active routes could represent the actual monetary demand, of which their inactivity can only be the possibility. Finally, this policy must always: • Minimize the age of all transaction outputs then determining the money supply, to keep money creation or destruction as timely as possible. • Avoid any extra rewards for concluding those active routes, to prevent the otherwise earned money from becoming an incentive to create it. Page 27 of 76 Adaptive Monetary Policy Proof-of-Loss Mirelo Deugh Ausgam Valis cba So, if the total number of monetary units is not that of all active spendable outputs at the smallest block depth with all its rights already surrendered, then the money supply must either expand or contract to make these two numbers coincide. Still: • The limits to its expansion and contraction must be the same, to avoid unduly prioritizing either variation. • For being a monetary cost, its contraction must not exceed all paid inactivity fees, which are then the only price paid for an oversupply of money. Hence, the money supply: 1. Can contract at most by all paid inactivity fees, which must alone cause this contraction by fractionally disappearing. 2. Can expand at most by all paid inactivity fees, which must alone cause this expansion by fractionally doubling. Then, at the current block height c, the systemic monetary surplus or deficit implicitly transferred to each route r must be: Vr = Vc × Fr Fc Where: • Vr means all money-supply variation transferred to r at c. • Vc means all money-supply variation at c. • Fr means all transaction-fee bids designated to r at c. • Fc means all transaction-fee bids at c. So money creation or destruction can only happen the first time all rewarded routes at each block height h have no rights earned at or below h left. Then, the money supply expands or contracts until its total number of units becomes that of all active routes at h, although never by more than all currently paid inactivity fees. This algorithm provides the following benefits: Page 28 of 76 Adaptive Monetary Policy Proof-of-Loss Mirelo Deugh Ausgam Valis cba • For being then randomly distributed, the proceeds of money creation will most likely not be an incentive to increase the number of spendable outputs per transaction. • The money supply will tend to be always free from its arbitrary manipulation, which will hardly reward its authors with more money than it has cost them. • Changes in monetary demand or even those in the velocity of money circulation are then unlikely to cause price volatility, which will tend to originate mostly from changes in: ⋄ The demand for priced items proportionally to their supply. ⋄ The productivity of any processes creating that supply. • For then resulting only from an increased route activity, all newly created money will be just profits, instead of also the revenue for paying mining expenses as in Bitcoin. So none of it will tend to cause price volatility by being readily spent. • Transaction fees need no longer transition from complementary to essential as in Bitcoin, with all associated risks. Instead, they will always be the primary reward, by at least reaching yet most likely exceeding - for including inactivity fees - and being more predictable than all simultaneously created money. • Monetary policy becomes necessary and adaptive instead of remaining arbitrary and unresponsive as in Bitcoin, by: ⋄ Targeting the number of all active routes at the smallest block depth with no rights left. ⋄ Transferring to each currently spendable output a systemic monetary surplus or deficit proportional and limited to all inactivity fees then proportionally destined to this route. So the money supply can grow from a single unit to any size and back with both its magnitude and allocation being minimally affected by people's wealth. Page 29 of 76 Adaptive Monetary Policy Proof-of-Loss Mirelo Deugh Ausgam Valis cba Block Validation Pseudocode SYNCHRONIZED() 1 if not DECLARED(hc) or not DECLARED(validated) 2 then return false 3 if hc = NIL or validated = NIL 4 then return false 5 return validated and 6 hc > GET_CURRENT_HEIGHT_FROM_PEERS() VALIDATE_CHAIN() 1 INITIALIZE() 2 label ERROR 3 UNDO_UNCOMMITED_PERSISTS_AND_REMOVES() 4 while ACCEPTING_CANDIDATE_BLOCKS() 5 do b← READ_NEWLY_RECEIVED_BLOCK() 6 if VALID_CANDIDATE_BLOCK(b) 7 then if READ_BLOCK_HEIGHT(b) > hc 8 then VALIDATE_BLOCK(b) 9 else VALIDATE_BRANCH(b) 10 PRUNE_ORPHANED_BRANCHES() INITIALIZE() 1 global branch_start ← chainer ← collectors ← NIL 2 global concluding_losses ← current_time ← Dq ← Fb ← NIL 3 global Fi ← hc ← highest_start ← hk ← hs ← hw ← Ib ← NIL 4 global implicit_balance ← input_depths ← inputs ← NIL 5 global lessee ← loss_decrease ← loss_height ← Mc ← NIL 6 global Mv ← open_asks ← quorum ← quorum_start ← NIL 7 global transaction_hashes ← validated ← voters ← Xc ← NIL 8 global Xs ← xg ← Zk ← Zt ← NIL Page 30 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALID_CANDIDATE_BLOCK(block) 1 if not VALID_BLOCK_FORMAT(block) or 2 not VALID_BLOCK_HASH(block) 3 then return false 4 SET_GLOBAL_STATE() 5 if READ_BLOCK_TIME(block) > current_time 6 then return false 7 if not NEW_OR_OPTIONAL_BLOCK(block) 8 then return false 9 if READ_BLOCK_HEIGHT(block) = hs 10 then return SET_DUMMY_PARENT(block) 11 return HAS_VALID_PARENT(block) VALID_BLOCK_HASH(block) 1 N ← GET_HASH(GET_BLOCK_HEADER_DATA(block)) 2 return READ_BLOCK_HASH(block) = N SET_GLOBAL_STATE(block) 1 if NIL = (hs ← RETRIEVE_STARTING_HEIGHT()) or 2 NIL = (hc ← RETRIEVE_CURRENT_HEIGHT()) or 3 NIL = (validated ← RETRIEVE_VALIDATED()) 4 then hs ← 1 5 hc ← 0 6 validated ← false 7 Ib ← BLOCK_TARGET_INTERVAL 8 current_time ← GET_CURRENT_TIME() 9 branch_start ← READ_BLOCK_HEIGHT(block) 10 quorum ← false Page 31 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba NEW_OR_OPTIONAL_BLOCK(block) 1 if hs > (h ← READ_BLOCK_HEIGHT(block)) 2 then return false 3 if h = hs 4 then return hs > hc 5 if (h− hc) > 1 6 then if hs > hc 7 then hs ← h 8 hc ← (h− 1) 9 return true 10 return false 11 if RETRIEVE_BLOCK(GET_BLOCK_IDENTIFIER(block)) 6= NIL 12 then return false 13 if IMMUTABLE_HEIGHT(h) 14 then return IMMUTABLE_BLOCK(block) 15 siblings ← RETRIEVE_BLOCKS_AT_HEIGHT(h) 16 r ← READ_CHAINER_IDENTIFIER(block) 17 another ← false 18 for each sibling in siblings 19 do if READ_CHAINER_IDENTIFIER(sibling) = r 20 then if h < hc or another 21 then return false 22 another ← true 23 return true GET_BLOCK_IDENTIFIER(block) 1 N ← READ_BLOCK_HASH(block) 2 height ← READ_BLOCK_HEIGHT(block) 3 return CREATE_BLOCK_IDENTIFIER(N, height) Page 32 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba IMMUTABLE_HEIGHT(height) 1 D← RETRIEVE_DUMMY_BLOCK() 2 blocks ← READ_IMMUTABLE_BLOCKS(D) 3 for each Xb in blocks 4 do if height = GET_BLOCK_HEIGHT(Xb) 5 then return true 6 return false IMMUTABLE_BLOCK(block) 1 D← RETRIEVE_DUMMY_BLOCK() 2 Xb ← GET_BLOCK_IDENTIFIER(block) 3 return EXISTS_IN_LIST(READ_IMMUTABLE_BLOCKS(D), Xb) SET_DUMMY_PARENT(block) 1 Xb ← GET_BLOCK_IDENTIFIER(block) 2 if NIL 6= (D ← GET_RESTORABLE_DUMMY_FROM_PEERS(Xb)) 3 then if VALID_DUMMY_PARENT(D, block) 4 then PERSIST_DUMMY_BLOCK(D) 5 PERSIST_STARTING_HEIGHT(hs) 6 PERSIST_VALIDATED(false) 7 return true 8 return false Page 33 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALID_DUMMY_PARENT(D, block) 1 if not VALID_DUMMY_FORMAT(D) or 2 not VALID_DUMMY_HASH(D) 3 then return false 4 if READ_DUMMY_TIME(D) > READ_BLOCK_TIME(block) 5 then return false 6 if GET_LENGTH(blocks ← READ_IMMUTABLE_BLOCKS(D)) = 0 7 then return false 8 h ← READ_BLOCK_HEIGHT(block) 9 heights ← {} 10 for each Xb in blocks 11 do if h > (height ← GET_BLOCK_HEIGHT(Xb)) 12 then return false 13 if height = h and GET_BLOCK_IDENTIFIER(block) 6= Xb 14 then return false 15 ADD_TO_LIST(heights, height) 16 heights ← ORDER_LIST(heights) 17 for each height in heights 18 do if height 6= h 19 then return false 20 h← (h + 1) 21 return true VALID_DUMMY_HASH(D) 1 N ← GET_HASH(GET_DUMMY_DATA(D)) 2 return READ_DUMMY_HASH(D) = N HAS_VALID_PARENT(block) 1 if NIL = (parent ← RETRIEVE_PARENT(block)) 2 then return false 3 time← READ_BLOCK_TIME(block) 4 return READ_BLOCK_TIME(parent) < time Page 34 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba RETRIEVE_PARENT(block) 1 Xb ← GET_PARENT_IDENTIFIER(block) 2 return RETRIEVE_BLOCK(Xb) GET_PARENT_IDENTIFIER(block) 1 N ← READ_PARENT_HASH(block) 2 height ← READ_BLOCK_HEIGHT(block) 3 return CREATE_BLOCK_IDENTIFIER(N, height− 1) VALIDATE_BLOCK(block) 1 if not IMMUTABLE_BLOCK(block) 2 then SET_CHAIN_STATE(block) 3 VALIDATE_BRANCH_LENGTH(block) 4 VALIDATE_CHAINER_ASK(block) 5 VALIDATE_OPTIONAL_HEADER(block) 6 VALIDATE_PROOF-OF-LOSS(block) 7 VALIDATE_LEASE(block) 8 VALIDATE_BLOCK_SIGNATURE(block) 9 VALIDATE_BLOCK_TRANSACTIONS(block) 10 VALIDATE_BLOCK_EARNINGS(block) 11 VALIDATE_GIFT_RIGHTS(block) 12 VALIDATE_CHAIN_PRUNING(block) 13 VALIDATE_CHAIN_STATE(block) 14 UPDATE_CHAIN(block) SET_CHAIN_STATE(block) 1 parent ← RETRIEVE_PARENT(block) 2 xg ← READ_GENERAL_MAX_HASH(parent) 3 if hs > (hw ← READ_ASKS_LOW(parent)) 4 then ERROR(CONFLICTING_ASKS_LOW) 5 if (hs − 1) > (hk ← READ_CHECKPOINT(parent)) 6 then ERROR(CONFLICTING_CHECKPOINT) 7 Mc ← READ_MONEY_SUPPLY(parent) 8 Mv ← 0 Page 35 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba ERROR(code) 1 DISPLAY(GET_ERROR_MESSAGE(code)) 2 goto ERROR VALIDATE_BRANCH_LENGTH(block) 1 if branch_start 6 hk 2 then ERROR(BRANCH_NOT_ABOVE_CHECKPOINT) VALIDATE_CHAINER_ASK(block) 1 if READ_ASKED_FEE_PER_BYTE(block) < 0 2 then ERROR(NEGATIVE_ASK) VALIDATE_OPTIONAL_HEADER(block) 1 if NIL = (H ← READ_OPTIONAL_HEADER(block)) 2 then return 3 if NIL 6= (lease ← READ_HEADER_LEASE(H)) 4 then VALIDATE_OPT-HEADER_LEASE(lease, H, block) 5 return 6 parent ← RETRIEVE_PARENT(block) 7 r ← READ_CHAINER_IDENTIFIER(parent) 8 VALIDATE_OPT-HEADER_CHAINER(H, r) 9 output ← GET_CONCLUDED_OUTPUT(r, parent) 10 VALIDATE_OPT-HEADER_IDENTIFIER(H, block, output) Page 36 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_OPT-HEADER_LEASE(lease, H, block) 1 VALIDATE_LEASE_PRICE(lease) 2 parent ← RETRIEVE_PARENT(block) 3 r ← READ_CHAINER_IDENTIFIER(parent) 4 if r = (rl ← READ_LESSEE_IDENTIFIER(lease)) 5 then ERROR(LEASE_TO_OPTIONAL_SELF) 6 if NIL 6= (output ← GET_CONCLUDED_OUTPUT(rl , parent)) 7 then VALIDATE_OPT-HEADER_CHAINER(H, r) 8 VALIDATE_OPT-HEADER_IDENTIFIER(H, block, output) 9 else if not LEASE-ONLY_OPT-HEADER(H) 10 then ERROR(UNNECESSARY_OPT-HEADER_DATA) 11 N ← GET_HASH(GET_LEASE_DATA(lease)) 12 if READ_LEASE_HASH(lease) 6= N 13 then ERROR(INVALID_OPT-HEADER_LEASE_HASH) 14 S← GET_LEASE-SIGNING_DATA(lease) 15 output ← GET_CONCLUDED_OUTPUT(r, parent) 16 if not VALID_SIGNATURE(output, N, S) 17 then ERROR(INVALID_OPT-HEADER_LEASE_SIGNATURE) VALIDATE_LEASE_PRICE(lease) 1 price ← READ_LEASE_PRICE(lease) 2 if price < 0 or price > 1 3 then ERROR(INVALID_LEASE_PRICE) GET_CONCLUDED_OUTPUT(r, block) 1 h← READ_BLOCK_HEIGHT(block) 2 while hs 6 (h ← (h− 1)) 3 do block ← RETRIEVE_PARENT(block) 4 if NIL 6= (output ← GET_OUTPUT(r, block)) 5 then return output 6 return NIL Page 37 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_OUTPUT(r, block) 1 t← GET_TRANSACTION_IDENTIFIER(r) 2 if NIL 6= (transaction ← GET_TRANSACTION(t, block)) 3 then n← GET_OUTPUT_INDEX(r) 4 i ← 0 5 for each output in transaction 6 do if n = (i ← (i + 1)) 7 then return output 8 return NIL GET_TRANSACTION(t, block) 1 for each transaction in block 2 do if GET_IDENTIFIER(transaction) = t 3 then return transaction 4 return NIL GET_IDENTIFIER(transaction) 1 N ← READ_HASH(transaction) 2 time← READ_TIME(transaction) 3 return CREATE_TRANSACTION_IDENTIFIER(N, time) VALIDATE_OPT-HEADER_CHAINER(H, r) 1 if READ_BLOCK-CHAINER_IDENTIFIER(H) 6= r 2 then ERROR(INVALID_OPT-HEADER_CHAINER) Page 38 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_OPT-HEADER_IDENTIFIER(H, block, output) 1 height ← READ_HEADER_HEIGHT(H) 2 if height 6= (READ_BLOCK_HEIGHT(block) − 1) 3 then ERROR(INVALID_OPT-HEADER_HEIGHT) 4 N ← GET_HASH(GET_HEADER_DATA(H)) 5 if READ_HEADER_HASH(H) 6= N 6 then ERROR(INVALID_OPT-HEADER_HASH) 7 if N = READ_PARENT_HASH(block) 8 then ERROR(INVALID_OPTIONAL_HEADER) 9 S← GET_HEADER-SIGNING_DATA(H) 10 if not VALID_SIGNATURE(output, N, S) 11 then ERROR(INVALID_OPT-HEADER_SIGNATURE) Page 39 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_PROOF-OF-LOSS(block) 1 h ← height ← READ_BLOCK_HEIGHT(block) 2 r ← READ_CHAINER_IDENTIFIER(b← block) 3 descending ← {} 4 chainer ← bw ← Xb ← NIL 5 bi ← 0 6 implicit_balance ← false 7 while chainer = NIL and hs 6 (h ← (h− 1)) 8 do if Xb 6= NIL 9 then ADD_TO_LIST(descending, Xb) 10 Xb ← GET_PARENT_IDENTIFIER(child ← b) 11 b← RETRIEVE_BLOCK(Xb) 12 if GET_INPUT(r, b) 6= NIL 13 then ERROR(TRANSFERRED_CHAINER) 14 if READ_CHAINER_IDENTIFIER(b) = r and bw = NIL 15 then bw ← b 16 if not implicit_balance 17 then bi ← GET_IMPLICIT_BALANCE(r, b, child) 18 chainer ← GET_OUTPUT(r, b) 19 if chainer = NIL 20 then ERROR(NONEXISTENT_CHAINER) 21 if bw = NIL 22 then bw ← b 23 be ← GET_EXPLICIT_BALANCE(chainer) 24 lr ← GET_ROUTE_LOSS(r, b) 25 ascending ← REVERT_LIST(descending) 26 VALIDATE_ROUTE(be, bi, lr, ascending) 27 Wr ← GET_REWARD(bw, NIL, lr, false) 28 dw ← (height − READ_BLOCK_HEIGHT(bw)) 29 dr ← (height − READ_BLOCK_HEIGHT(b)) 30 xr ← GET_MAX_HASH(Wr, lr, dr, dw) 31 VALIDATE_PROOF-OF-LOSS_HASH(block, xr) Page 40 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_INPUT(r, block) 1 for each transaction in block 2 do for each input in transaction 3 do if GET_ROUTE_IDENTIFIER(input) = r 4 then return input 5 return NIL GET_ROUTE_IDENTIFIER(input) 1 N ← READ_TRANSACTION_HASH(input) 2 time← READ_TRANSACTION_TIME(input) 3 t← CREATE_TRANSACTION_IDENTIFIER(N, time) 4 n← READ_OUTPUT_INDEX(input) 5 return CREATE_ROUTE_IDENTIFIER(t, n) GET_IMPLICIT_BALANCE(r, block, child) 1 earnings ← READ_EARNINGS_PER_OUTPUT(block) 2 rw ← GET_PROXY_CHAINER(block) 3 rl ← GET_PROXY_LESSEE(block) 4 seized ← HAS_TWO_HEADERS(child) 5 for each output_quota in earnings 6 do if r = (rq ← READ_EARNER_IDENTIFIER(output_quota)) 7 then implicit_balance ← true 8 if seized and (rq = rw or rq = rl) 9 then return 0 10 return READ_OUTPUT_EARNINGS(output_quota) 11 return 0 GET_PROXY_CHAINER(r, block) 1 r ← READ_CHAINER_IDENTIFIER(block) 2 return GET_LAST_PROXY(r, block) Page 41 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_LAST_PROXY(r, block) 1 while NIL 6= (input ← GET_INPUT(r, block)) 2 do if NIL = (r ← GET_PROXY_IDENTIFIER(r, block)) 3 then ERROR(MISSING_PROXY_ROUTE) 4 return r GET_PROXY_IDENTIFIER(r, block) 1 p← GET_BLOCK_PROXIES(block) 2 return READ_PROXY_IDENTIFIER(p, r) GET_BLOCK_PROXIES(block) 1 Xb ← GET_BLOCK_IDENTIFIER(block) 2 if NIL = (p ← RETRIEVE_PROXY_MAP(Xb)) 3 then p← CREATE_PROXY_MAP(block) 4 PERSIST_PROXY_MAP(Xb , p) 5 return p CREATE_PROXY_MAP(block) 1 p← CREATE_EMPTY_PROXY_MAP() 2 for each transaction in block 3 do for each input in transaction 4 do if NIL 6= (n← READ_PROXY-ROUTE_INDEX(input)) 5 then SET_PROXY(p, n, input, transaction) 6 return p Page 42 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba SET_PROXY(p, n, input, transaction) 1 r ← GET_ROUTE_IDENTIFIER(input) 2 t← GET_IDENTIFIER(transaction) 3 i ← 0 4 for each output in transaction 5 do if n = (i ← (i + 1)) 6 then if READ_EXPLICIT_BALANCE(output) = 0 7 then ERROR(PROVABLY_UNSPENDABLE_PROXY) 8 rp ← CREATE_ROUTE_IDENTIFIER(t, n) 9 WRITE_PROXY_IDENTIFIER(p, r, rp) 10 return 11 ERROR(NONEXISTENT_PROXY_ROUTE) GET_PROXY_LESSEE(r, block) 1 if NIL 6= (lease ← READ_LEASE(block)) 2 then r ← READ_LESSEE_IDENTIFIER(lease) 3 return GET_LAST_PROXY(r, block) 4 return NIL GET_EXPLICIT_BALANCE(output) 1 if 0 = (be ← READ_EXPLICIT_BALANCE(output)) 2 then ERROR(PROVABLY_UNSPENDABLE_OUTPUT) 3 return be GET_ROUTE_LOSS(r, block) 1 t← GET_TRANSACTION_IDENTIFIER(r) 2 transaction ← GET_TRANSACTION(t, block) 3 n← GET_OUTPUT_INDEX(r) 4 return GET_LOSS(n, transaction) Page 43 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_LOSS(n, transaction) 1 zt ← GET_SIZE_IN_BYTES(transaction) 2 zr ← Zr ← i ← 0 3 for each output in transaction 4 do i← (i + 1) 5 if READ_EXPLICIT_BALANCE(output) > 0 6 then Zr ← (Zr + (z← GET_SIZE_IN_BYTES(output))) 7 if n = i 8 then zr ← z 9 return zt × (zr ÷ Zr) VALIDATE_ROUTE(be, bi, lr, ascending) 1 if GET_INACTIVITY_FEES(lr, ascending) > (be + bi) 2 then ERROR(SPENT_OUTPUT) GET_INACTIVITY_FEES(lr, ascending) 1 losses ← Ft ← 0 2 bids ← {} 3 for each Xb in ascending 4 do block ← RETRIEVE_BLOCK(Xb) 5 Lc ← READ_CURRENT_LOSS(block) 6 for each transaction in block 7 do losses ← (losses + GET_SIZE_IN_BYTES(transaction)) 8 ft ← READ_BID_FEE_PER_BYTE(transaction) 9 ADD_TO_LIST(bids, ft) 10 if losses > Lc 11 then Ft ← (Ft + GET_MEDIAN(bids)) 12 losses ← 0 13 bids ← {} 14 return Ft × lr Page 44 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_MEDIAN(list) 1 list← ORDER_LIST(list) 2 remainder ← ((length ← GET_LENGTH(list)) mod 2) 3 start ← (TRIM_TO_INTEGER(length ÷ 2) + remainder) 4 start ← (LISTS_FIRST_INDEX + (start − 1)) 5 if remainder = 1 6 then return READ_ELEMENT(list, start) 7 e1 ← READ_ELEMENT(list, start) 8 e2 ← READ_ELEMENT(list, start + 1) 9 return (e1 + e2)÷ 2 GET_REWARD(block, child, lr, seizing) 1 if child 6= NIL 2 then if HAS_TWO_HEADERS(child) 3 then return 0 4 if READ_BLOCK_HEIGHT(block) > hs 5 then parent ← RETRIEVE_PARENT(block) 6 if seizing and HAS_TWO_HEADERS(block) 7 then l ← GET_CHAINER_LOSS(parent) 8 Wr ← GET_REWARD(parent, NIL, l, true) 9 else Wr ← READ_GIFT_RIGHTS(parent) 10 else D ← RETRIEVE_DUMMY_BLOCK() 11 if seizing and HAS_TWO_HEADERS(block) 12 then Wr ← READ_DUMMY_REWARD(D) 13 else Wr ← READ_DUMMY_GIFT_RIGHTS(D) 14 for each transaction in block 15 do Wr ← (Wr + GET_SIZE_IN_BYTES(transaction)) 16 return Wr + lr Page 45 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_CHAINER_LOSS(block) 1 r ← READ_CHAINER_IDENTIFIER(block) 2 t← GET_TRANSACTION_IDENTIFIER(r) 3 h ← READ_BLOCK_HEIGHT(b← block) 4 transaction ← NIL 5 while transaction = NIL and hs 6 (h ← (h− 1)) 6 do b← RETRIEVE_PARENT(b) 7 transaction ← GET_TRANSACTION(t, b) 8 if transaction 6= NIL 9 then loss_height ← READ_BLOCK_HEIGHT(b) 10 n← GET_OUTPUT_INDEX(r) 11 return GET_LOSS(n, transaction) 12 D ← RETRIEVE_DUMMY_BLOCK() 13 Xb ← GET_BLOCK_IDENTIFIER(block) 14 if NIL = (lr ← READ_CHAINER_LOSS(D, Xb)) 15 then ERROR(MISSING_CHAINER_LOSS) 16 if NIL = (loss_height ← READ_LOSS_HEIGHT(D, Xb)) 17 then ERROR(MISSING_CHAINER-LOSS_HEIGHT) 18 return lr GET_MAX_HASH(Wr, lr, dr, dw) 1 if Wr < (Ws ← (dw × lr)) 2 then Ws ←Wr 3 m1 ← ((lr ÷Wr) + ((1− (lr ÷Wr))× (Ws ÷Wr))) 4 m2 ← (((Ws ÷ lr) + 1)÷ dr) 5 return xg × lr ×m1 ×m2 VALIDATE_PROOF-OF-LOSS_HASH(block, xr) 1 VALIDATE_PROOF-OF-LOSS_CONTEXT(block) 2 N ← GET_HASH(GET_PROOF-OF-LOSS_DATA(block)) 3 if READ_PROOF-OF-LOSS_HASH(block) 6= N 4 then ERROR(INVALID_PROOF-OF-LOSS_HASH) 5 if N > xr 6 then ERROR(LOW_PROOF-OF-LOSS_DIFFICULTY) Page 46 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_PROOF-OF-LOSS_CONTEXT(block) 1 h ← height ← READ_BLOCK_HEIGHT(b← block) 2 while hw 6 (h ← (h− 1)) 3 do b← RETRIEVE_PARENT(b) 4 lr ← GET_CHAINER_LOSS(b) 5 dr ← (height − loss_height) 6 r ← READ_CHAINER_IDENTIFIER(b) 7 rw ← NIL 8 b← block 9 while rw 6= r 10 do b← RETRIEVE_PARENT(b) 11 rw ← READ_CHAINER_IDENTIFIER(b) 12 Wr ← GET_REWARD(b, NIL, lr, false) 13 dw ← (height − READ_BLOCK_HEIGHT(b)) 14 xc ← GET_MAX_HASH(Wr, lr, dr, dw) 15 if READ_PROOF-OF-LOSS_CONTEXT(block) 6= xc 16 then ERROR(INVALID_PROOF-OF-LOSS_CONTEXT) Page 47 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_LEASE(block) 1 if (lessee ← NIL) = (lease ← READ_LEASE(block)) 2 then return 3 VALIDATE_LEASE_PRICE(lease) 4 r ← READ_LESSEE_IDENTIFIER(lease) 5 if r = READ_CHAINER_IDENTIFIER(block) 6 then ERROR(LEASE_TO_SELF) 7 h ← READ_BLOCK_HEIGHT(block) 8 descending ← {} 9 Xb ← NIL 10 bi ← 0 11 implicit_balance ← false 12 while lessee = NIL and hs 6 (h ← (h− 1)) 13 do if Xb 6= NIL 14 then ADD_TO_LIST(descending, Xb) 15 Xb ← GET_PARENT_IDENTIFIER(child ← block) 16 block ← RETRIEVE_BLOCK(Xb) 17 if GET_INPUT(r, block) 6= NIL 18 then ERROR(TRANSFERRED_LESSEE) 19 if not implicit_balance 20 then bi ← GET_IMPLICIT_BALANCE(r, block, child) 21 lessee ← GET_OUTPUT(r, block) 22 if lessee = NIL 23 then ERROR(NONEXISTENT_LESSEE) 24 be ← GET_EXPLICIT_BALANCE(lessee) 25 lr ← GET_ROUTE_LOSS(r, block) 26 ascending ← REVERT_LIST(descending) 27 VALIDATE_ROUTE(be, bi, lr, ascending) 28 N ← GET_HASH(GET_LEASE_DATA(lease)) 29 if READ_LEASE_HASH(lease) 6= N 30 then ERROR(INVALID_LEASE_HASH) 31 S← GET_LEASE-SIGNING_DATA(lease) 32 if not VALID_SIGNATURE(chainer, N, S) 33 then ERROR(INVALID_LEASE_SIGNATURE) Page 48 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_BLOCK_SIGNATURE(block) 1 if NIL = (output ← lessee) 2 then output ← chainer 3 H ← READ_HEADER(block) 4 N ← GET_HASH(GET_HEADER_DATA(H)) 5 if READ_HEADER_HASH(H) 6= N 6 then ERROR(INVALID_BLOCK_HASH) 7 S← GET_HEADER-SIGNING_DATA(H) 8 if not VALID_SIGNATURE(output, N, S) 9 then ERROR(INVALID_BLOCK_SIGNATURE) VALIDATE_BLOCK_TRANSACTIONS(block) 1 Zt ← loss_decrease ← Fb ← Fi ← 0 2 FORGET_ROUTE_EARNINGS() 3 inputs ← {} 4 input_depths ← {} 5 concluding_losses ← {} 6 transaction_hashes ← {} 7 collectors ← {} 8 for each transaction in block 9 do VALIDATE_TRANSACTION(transaction, block) 10 N ← GET_HASH-TREE_ROOT(transaction_hashes) 11 if READ_TRANSACTIONS_HASH-TREE_ROOT(block) 6= N 12 then ERROR(INVALID_TRANSACTIONS_HASH-TREE_ROOT) 13 parent ← RETRIEVE_PARENT(block) 14 Lc ← READ_CURRENT_LOSS(parent) 15 if READ_CURRENT_LOSS(block) 6= (Lc + (Zt − loss_decrease)) 16 then ERROR(INVALID_CURRENT_LOSS) Page 49 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_TRANSACTION(transaction, block) 1 VALIDATE_TRANSACTION_FORMAT(transaction) 2 VALIDATE_TRANSACTION_IDENTIFIER(transaction, block) 3 Bt ← Be ← Bi ← Fr ← 0 4 for each output in transaction 5 do Bt ← (Bt + GET_OUTPUT_BALANCE(output)) 6 SET_PROXY_BALANCES(transaction, block) 7 height ← READ_BLOCK_HEIGHT(block) 8 for each input in transaction 9 do b← block 10 r ← GET_ROUTE_IDENTIFIER(input) 11 descending ← {} 12 Xb ← NIL 13 while NIL = (output ← GET_OUTPUT(r, b)) 14 do if Xb 6= NIL 15 then ADD_TO_LIST(descending, Xb) 16 Xb ← GET_PARENT_IDENTIFIER(b) 17 b← RETRIEVE_BLOCK(Xb) 18 Be ← (Be + (be ← GET_EXPLICIT_BALANCE(output))) 19 Bi ← (Bi + (bi ← GET_PROXY_BALANCE(r, b))) 20 lr ← GET_ROUTE_LOSS(r, b) 21 ascending ← REVERT_LIST(descending) 22 Fr ← (Fr + ( fr ← GET_INACTIVITY_FEES(lr, ascending))) 23 VALIDATE_INPUT(input, be, bi, fr, output, transaction) 24 loss_decrease ← (loss_decrease + lr) 25 if 0 < (dr ← (height − READ_BLOCK_HEIGHT(b))) 26 then ADD_TO_LIST(input_depths, dr) 27 VALIDATE_BID(transaction, Bt, Be, Bi, Fr) Page 50 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_TRANSACTION_IDENTIFIER(transaction, block) 1 N ← GET_HASH(GET_TRANSACTION_DATA(transaction)) 2 if READ_HASH(transaction) 6= N 3 then ERROR(INVALID_TRANSACTION_HASH) 4 if EXISTS_IN_LIST(transaction_hashes, N) 5 then ERROR(DUPLICATE_TRANSACTION_HASH) 6 ADD_TO_LIST(transaction_hashes, N) 7 time ← READ_TIME(transaction) 8 if time > READ_BLOCK_TIME(block) 9 then ERROR(LATE_TRANSACTION) 10 for each input in transaction 11 do if time < READ_TRANSACTION_TIME(input) 12 then ERROR(EARLY_TRANSACTION) 13 t← CREATE_TRANSACTION_IDENTIFIER(N, time) 14 h ← READ_BLOCK_HEIGHT(block) 15 while hs 6 (h ← (h− 1)) 16 do block ← RETRIEVE_PARENT(block) 17 if time > READ_BLOCK_TIME(block) 18 then return 19 if GET_TRANSACTION(t, block) 6= NIL 20 then ERROR(DUPLICATE_TRANSACTION) GET_OUTPUT_BALANCE(output) 1 if 0 > (be ← READ_EXPLICIT_BALANCE(output)) 2 then ERROR(NEGATIVE_OUTPUT_BALANCE) 3 if PROVABLY_UNSPENDABLE(output) 4 then if be > 0 5 then ERROR(ILLEGAL_MONEY_DESTRUCTION) 6 else if be = 0 7 then ERROR(NULL_SPENDABLE_BALANCE) 8 return be Page 51 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba SET_PROXY_BALANCES(transaction, block) 1 for each input in transaction 2 do r← GET_ROUTE_IDENTIFIER(input) 3 if 0 < (bi ← GET_ROUTE_BALANCE(r, block)) 4 then SET_BALANCES(input, bi, block) GET_ROUTE_BALANCE(r, block) 1 hp ← ((h ← READ_BLOCK_HEIGHT(block)) − 1) 2 bi ← bs ← 0 3 implicit_balance ← false 4 while NIL = (output ← GET_OUTPUT(r, block)) 5 do if hs > (h ← (h− 1)) 6 then ERROR(NONEXISTENT_OUTPUT) 7 block ← RETRIEVE_PARENT(child ← block) 8 if GET_INPUT(r, block) 6= NIL 9 then ERROR(TRANSFERRED_OUTPUT) 10 if h = hp 11 then bs ← GET_SEIZED_BALANCE(r, block, child) 12 if not implicit_balance 13 then bi ← GET_IMPLICIT_BALANCE(r, block, child) 14 return bi + bs Page 52 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_SEIZED_BALANCE(r, block, child) 1 if not HAS_TWO_HEADERS(child) or 2 0 = (ratio ← GET_SEIZING_RATIO(r, child)) 3 then return 0 4 earnings ← READ_EARNINGS_PER_OUTPUT(block) 5 bs ← 0 6 rw ← GET_PROXY_CHAINER(block) 7 rl ← GET_PROXY_LESSEE(block) 8 for each output_quota in earnings 9 do rq ← READ_EARNER_IDENTIFIER(output_quota) 10 if rq = rw or rq = rl 11 then bi ← READ_OUTPUT_EARNINGS(output_quota) 12 bs ← (bs + (bi × ratio)) 13 if bi > 0 and GET_INPUT(rq, child) = NIL 14 then SET_ROUTE_BALANCE(rq, 0) 15 return bs GET_SEIZING_RATIO(r, block) 1 ratio ← 1 2 if NIL 6= (lease ← READ_LEASE(block)) 3 then ratio ← READ_LEASE_PRICE(lease) 4 if r = READ_LESSEE_IDENTIFIER(lease) 5 then return 1− ratio 6 if r = READ_CHAINER_IDENTIFIER(block) 7 then return ratio 8 return 0 SET_ROUTE_BALANCE(r, bi) 1 MEMORIZE_ROUTE_EARNINGS(r, bi) 2 if not EXISTS_IN_LIST(collectors, r) 3 then ADD_TO_LIST(collectors, r) Page 53 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba SET_BALANCES(input, bi, block) 1 r ← GET_ROUTE_IDENTIFIER(input) 2 SET_ROUTE_EARNINGS(r, bi) 3 rp ← GET_PROXY_IDENTIFIER(r, block) 4 while rp 6= NIL and rp 6= r 5 do SET_ROUTE_EARNINGS(rp, bi) 6 if NIL 6= (input ← GET_INPUT(r ← rp, block)) 7 then rp ← GET_PROXY_IDENTIFIER(r, block) 8 else if not EXISTS_IN_LIST(collectors, rp) 9 then ADD_TO_LIST(collectors, rp) SET_ROUTE_EARNINGS(r, bi) 1 if NIL 6= (v ← RECALL_ROUTE_EARNINGS(r)) 2 then bi ← (bi + v) 3 MEMORIZE_ROUTE_EARNINGS(r, bi) GET_PROXY_BALANCE(r, block) 1 if GET_PROXY_IDENTIFIER(r, block) = NIL and 2 NIL 6= (bi ← RECALL_ROUTE_EARNINGS(r)) 3 then return bi 4 return 0 VALIDATE_INPUT(input, be, bi, fr, output, transaction) 1 if fr > (be + bi) 2 then ERROR(SPENT_INPUT) 3 N ← READ_HASH(transaction) 4 S← GET_TRANSACTION-SIGNING_DATA(input, transaction) 5 if not VALID_SIGNATURE(output, N, S) 6 then ERROR(INVALID_TRANSACTION_SIGNATURE) 7 r ← GET_ROUTE_IDENTIFIER(input) 8 if EXISTS_IN_LIST(inputs, r) 9 then ERROR(DUPLICATE_INPUT) 10 ADD_TO_LIST(inputs, r) Page 54 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_BID(transaction, Bt, Be, Bi, Fr) 1 if Bt = 0 2 then ERROR(ROUTELESS_TRANSACTION) 3 ft ← READ_BID_FEE_PER_BYTE(transaction) 4 zt ← GET_SIZE_IN_BYTES(transaction) 5 if Fr > (bid ← ( ft × zt)) 6 then ERROR(LOW_BID) 7 if (Bt + bid) 6= (Be + Bi) 8 then ERROR(INEXACT_SPENDING) 9 Fb ← (Fb + bid) 10 Zt ← (Zt + zt) 11 ADD_TO_LIST(concluding_losses, zt) 12 Fi ← (Fi + Fr) VALIDATE_BLOCK_EARNINGS(block) 1 Zk ← 0 2 FORGET_FAILING_TO_SELL() 3 asks ← GET_ASKS(block) 4 if Zt > Zk 5 then ERROR(UNAUTHORIZED_LOSSES) 6 Fz ← 0 7 for each ask in asks 8 do if NIL 6= (zk ← READ_SOURCE_REWARD(ask)) 9 then k← GET_SOURCE_IDENTIFIER(ask) 10 zb ← ((zk ÷ Zk)× Zt) 11 MEMORIZE_FAILING_TO_SELL(k, zk − zb) 12 zb ← ((READ_RIGHTS_FOR_SALE(ask) ÷ Zk)× Zt) 13 fk ← READ_ASKED_FEES(ask) 14 WRITE_CHARGED_FEES(ask, fz ← ( fk × zb)) 15 Fz ← (Fz + fz) 16 if Fb < Fz 17 then ERROR(UNPAID_FEES) 18 APPLY_PRIORITIZATION(asks, block, Fz) 19 APPLY_MONETARY_POLICY(asks, block) 20 VALIDATE_EARNINGS(block, asks) Page 55 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_ASKS(block) 1 hp ← ((h ← READ_BLOCK_HEIGHT(b← block)) − 1) 2 asks ← {} 3 open_asks ← {} 4 blocks ← {} 5 FORGET_REVOKED_RIGHTS() 6 FORGET_CONCLUDED_LOSSES() 7 Xb ← NIL 8 while hw 6 (h ← (h− 1)) 9 do if Xb 6= NIL 10 then ADD_TO_LIST(blocks, Xb) 11 Xb ← GET_PARENT_IDENTIFIER(child ← b) 12 b← RETRIEVE_BLOCK(Xb) 13 lr ← GET_CHAINER_LOSS(b) 14 Wr ← GET_REWARD(b, child, lr, true) 15 if Wr > (Ws ← ((hp − h)× lr)) 16 then if lr < (zk ← (Wr −Ws)) 17 then zk ← lr 18 r ← READ_CHAINER_IDENTIFIER(b) 19 k← CREATE_ASK_IDENTIFIER(h, r) 20 SET_REVOKED_RIGHTS(k, blocks) 21 MEMORIZE_CONCLUDED_LOSS(r, lr) 22 ADD_TO_LIST(open_asks, k) 23 ADD_ASKS(b, zk, block, asks) 24 return asks SET_REVOKED_RIGHTS(k, blocks) 1 Wk ← 0 2 for each Xb in blocks 3 do b← RETRIEVE_BLOCK(Xb) 4 if NIL 6= (wk ← READ_REVOKED_RIGHTS(b, k)) 5 then Wk ← (Wk + wk) 6 MEMORIZE_REVOKED_RIGHTS(k, Wk) Page 56 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba ADD_ASKS(b, zk, block, asks) 1 h ← READ_BLOCK_HEIGHT(b) 2 r ← READ_CHAINER_IDENTIFIER(b) 3 if EXISTS_IN_LIST(READ_EXCLUDED_ASK_HEIGHTS(block), h) 4 then k← CREATE_ASK_IDENTIFIER(h, r) 5 MEMORIZE_FAILING_TO_SELL(k, zk) 6 return 7 fk ← READ_ASKED_FEE_PER_BYTE(b) 8 Zk ← (Zk + (zs ← zk)) 9 if NIL 6= (lease ← READ_LEASE(b)) 10 then rl ← READ_LESSEE_IDENTIFIER(lease) 11 rp ← GET_PROXY(rl , block, h) 12 SET_BALANCE(rp, block) 13 k← CREATE_ASK_IDENTIFIER(h, rp) 14 zs ← (zk × (price ← READ_PRICE(lease))) 15 ask ← CREATE_ASK(k, fk, zk × (1− price), NIL, r) 16 ADD_TO_LIST(asks, ask) 17 rp ← GET_PROXY(r, block, h) 18 SET_BALANCE(rp, block) 19 k← CREATE_ASK_IDENTIFIER(h, rp) 20 ask ← CREATE_ASK(k, fk, zs, zk, r) 21 ADD_TO_LIST(asks, ask) GET_PROXY(r, block, height) 1 h ← READ_BLOCK_HEIGHT(b← block) 2 descending ← {} 3 while height 6 (h ← (h− 1)) 4 do Xb ← GET_PARENT_IDENTIFIER(b) 5 ADD_TO_LIST(descending, Xb) 6 b← RETRIEVE_BLOCK(Xb) 7 ascending ← REVERT_LIST(descending) 8 for each Xb in ascending 9 do b← RETRIEVE_BLOCK(Xb) 10 r ← GET_LAST_PROXY(r, b) 11 return GET_LAST_PROXY(r, block) Page 57 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba SET_BALANCE(r, block) 1 if RECALL_ROUTE_EARNINGS(r) = NIL 2 then bi ← GET_ROUTE_BALANCE(r, block) 3 SET_ROUTE_BALANCE(r, bi) CREATE_ASK(k, fk, zs, zk, r) 1 ask ← CREATE_EMPTY_ASK() 2 WRITE_ASK_IDENTIFIER(ask, k) 3 WRITE_ASKED_FEES(ask, fk) 4 WRITE_RIGHTS_FOR_SALE(ask, zs) 5 WRITE_SOURCE_REWARD(ask, zk) 6 WRITE_OWNER_IDENTIFIER(ask, r) 7 WRITE_CHARGED_FEES(ask, NIL) 8 WRITE_EARNINGS(ask, NIL) 9 return ask GET_SOURCE_IDENTIFIER(ask) 1 h← GET_HEIGHT(READ_ASK_IDENTIFIER(ask)) 2 r ← READ_OWNER_IDENTIFIER(ask) 3 return CREATE_ASK_IDENTIFIER(h, r) Page 58 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba APPLY_PRIORITIZATION(asks, block, Fz) 1 seized ← 0 2 if Fz > 0 3 then earnings ← {} 4 for each ask in asks 5 do if NIL 6= (zk ← READ_SOURCE_REWARD(ask)) 6 then zb ← ((zk ÷ Zk)× Zt) 7 fz ← (READ_ASKED_FEES(ask) × zb) 8 ADD_TO_LIST(earnings, Fb × ( fz ÷ Fz)) 9 priority ← GET_PRIORITIZATION_QUOTIENT() 10 seized ← (GET_MEDIAN(earnings) × priority) 11 if seized > Fb 12 then seized ← Fb 13 remnant ← (Fb − seized) 14 for each ask in asks 15 do fz ← READ_CHARGED_FEES(ask) 16 WRITE_EARNINGS(ask, remnant × ( fz ÷ Fz)) 17 h ← READ_BLOCK_HEIGHT(block) 18 r ← READ_CHAINER_IDENTIFIER(block) 19 fs ← seized 20 if NIL 6= (lease ← READ_LEASE(block)) 21 then rl ← READ_LESSEE_IDENTIFIER(lease) 22 rp ← GET_LAST_PROXY(rl , block) 23 SET_BALANCE(rp, block) 24 k← CREATE_ASK_IDENTIFIER(h, rp) 25 ask ← CREATE_ASK(k, NIL, NIL, NIL, NIL) 26 fs ← (seized× (price ← READ_PRICE(lease))) 27 WRITE_EARNINGS(ask, seized× (1− price)) 28 ADD_TO_LIST(asks, ask) 29 rp ← GET_LAST_PROXY(r, block) 30 SET_BALANCE(rp, block) 31 k← CREATE_ASK_IDENTIFIER(h, rp) 32 ask ← CREATE_ASK(k, NIL, NIL, NIL, NIL) 33 WRITE_EARNINGS(ask, fs) 34 ADD_TO_LIST(asks, ask) Page 59 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_PRIORITIZATION_QUOTIENT() 1 average_depth ← GET_AVERAGE(input_depths) 2 median_depth ← GET_MEDIAN(input_depths) 3 depth_quotient ← (average_depth ÷median_depth) 4 average_loss ← GET_AVERAGE(concluding_losses) 5 median_loss ← GET_MEDIAN(concluding_losses) 6 loss_quotient ← (median_loss÷ average_loss) 7 return depth_quotient × loss_quotient GET_AVERAGE(list) 1 return GET_SUM(list)÷ GET_LENGTH(list) APPLY_MONETARY_POLICY(asks, block) 1 if Mc = (target ← GET_MONETARY_TARGET(block)) 2 then return 3 if 0 < (Mv ← (target −Mc)) 4 then if Mv > Fi 5 then Mv ← Fi 6 else if 0 > (Fi + Mv) 7 then Mv ← (0− Fi) 8 for each ask in asks 9 do fb ← READ_EARNINGS(ask) 10 WRITE_EARNINGS(ask, fb + (Mv × ( fb ÷ Fb))) Page 60 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_MONETARY_TARGET(block) 1 if hw = hs 2 then return Mc 3 h ← READ_BLOCK_HEIGHT(block) 4 losses ← Lc ← 0 5 descending ← {} 6 hm ← (hw − 1) 7 while h > hw or losses < Lc 8 do if h = hs 9 then ERROR(CONFLICTING_LOSSES) 10 Xb ← GET_PARENT_IDENTIFIER(block) 11 block ← RETRIEVE_BLOCK(Xb) 12 if hw > (h ← (h− 1)) 13 then if h = hm 14 then Lc ← READ_CURRENT_LOSS(block) 15 ADD_TO_LIST(descending, Xb) 16 for each transaction in block 17 do zt ← GET_SIZE_IN_BYTES(transaction) 18 losses ← (losses + zt) 19 ascending ← REVERT_LIST(descending) 20 return GET_LENGTH(GET_ROUTES(ascending)) GET_ROUTES(ascending) 1 routes ← {} 2 for each Xb in ascending 3 do block ← RETRIEVE_BLOCK(Xb) 4 for each transaction in block 5 do ADD_ROUTES(routes, transaction) 6 DROP_ROUTES(routes, transaction) 7 return routes Page 61 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba ADD_ROUTES(routes, transaction) 1 t← GET_IDENTIFIER(transaction) 2 n← 0 3 for each output in transaction 4 do n← (n + 1) 5 if READ_EXPLICIT_BALANCE(output) > 0 6 then r← CREATE_ROUTE_IDENTIFIER(t, n) 7 ADD_TO_LIST(routes, r) DROP_ROUTES(routes, transaction) 1 for each input in transaction 2 do r← GET_ROUTE_IDENTIFIER(input) 3 if EXISTS_IN_LIST(routes, r) 4 then DROP_FROM_LIST(routes, r) VALIDATE_EARNINGS(block, asks) 1 proxy_asks ← GET_PROXY_ASKS(asks, block) 2 earnings ← READ_EARNINGS_PER_OUTPUT(block) 3 if GET_LENGTH(earnings) 6= GET_LENGTH(proxy_asks) 4 then ERROR(INVALID_COLLECTORS_NUMBER) 5 for each ask in proxy_asks 6 do VALIDATE_OUTPUT_EARNINGS(earnings, ask) Page 62 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_PROXY_ASKS(asks, block) 1 FORGET_PROXY_ASKS() 2 for each ask in asks 3 do SET_PROXY_ASK(ask) 4 proxy_asks ← {} 5 for each ask in asks 6 do r ← GET_EARNER_IDENTIFIER(ask) 7 if not INCLUDED_PROXY_ASK(r, proxy_asks) and 8 NIL 6= (proxy ← RECALL_PROXY_ASK(r)) 9 then ADD_TO_LIST(proxy_asks, proxy) 10 h ← READ_BLOCK_HEIGHT(block) 11 for each r in collectors 12 do k← CREATE_ASK_IDENTIFIER(h, r) 13 ask ← CREATE_ASK(k, NIL, NIL, NIL, NIL) 14 WRITE_EARNINGS(ask, RECALL_ROUTE_EARNINGS(r)) 15 ADD_TO_LIST(proxy_asks, ask) 16 return proxy_asks SET_PROXY_ASK(ask) 1 if 0 = ( fb ← READ_EARNINGS(ask)) 2 then return 3 r ← GET_EARNER_IDENTIFIER(ask) 4 if NIL = (proxy ← RECALL_PROXY_ASK(r)) 5 then if NIL 6= (bi ← RECALL_ROUTE_EARNINGS(r)) 6 then WRITE_EARNINGS(ask, fb + bi) 7 if EXISTS_IN_LIST(collectors, r) 8 then DROP_FROM_LIST(collectors, r) 9 MEMORIZE_PROXY_ASK(r, ask) 10 return 11 WRITE_EARNINGS(proxy, fb + READ_EARNINGS(proxy)) GET_EARNER_IDENTIFIER(ask) 1 k← READ_ASK_IDENTIFIER(ask) 2 return GET_ASKER(k) Page 63 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba INCLUDED_PROXY_ASK(r, proxy_asks) 1 for each ask in proxy_asks 2 do if GET_EARNER_IDENTIFIER(ask) = r 3 then return true 4 return false VALIDATE_OUTPUT_EARNINGS(earnings, ask) 1 r ← GET_EARNER_IDENTIFIER(ask) 2 fb ← READ_EARNINGS(ask) 3 for each output_quota in earnings 4 do if READ_EARNER_IDENTIFIER(output_quota) = r and 5 READ_OUTPUT_EARNINGS(output_quota) = fb 6 then return 7 ERROR(INVALID_OUTPUT_EARNINGS) VALIDATE_GIFT_RIGHTS(block) 1 Wg ← 0 2 for each k in open_asks 3 do wu ← RECALL_FAILING_TO_SELL(k) 4 Wk ← RECALL_REVOKED_RIGHTS(k) 5 l ← RECALL_CONCLUDED_LOSS(GET_ASKER(k)) 6 if wu < (wk ← ((2× l)−Wk)) 7 then wk ← wu 8 Wg ← (Wg + (wu −wk)) 9 VALIDATE_REVOKED_RIGHTS(k, wk, block) 10 if READ_GIFT_RIGHTS(block) 6= Wg 11 then ERROR(INVALID_GIFT_RIGHTS) VALIDATE_REVOKED_RIGHTS(k, wk, block) 1 if wk = 0 2 then wk ← NIL 3 if READ_REVOKED_RIGHTS(block, k) 6= wk 4 then ERROR(INVALID_REVOKED_RIGHTS) Page 64 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_CHAIN_PRUNING(block) 1 SET_PRUNING_STATE(block) 2 votes ← {} 3 quorum_start ← hs 4 for each Xb in voters 5 do b← RETRIEVE_BLOCK(Xb) 6 ADD_PRUNING_VOTE(votes, b) 7 if quorum ← (GET_LENGTH(votes) > GET_QUORUM()) 8 then quorum_start ← GET_QUORUM_START(votes) 9 if quorum_start > hs and validated 10 then Dq ← CREATE_DUMMY_BLOCK(block) 11 else if quorum_start = hs 12 then VALIDATE_HIGHEST_VOTER(block) 13 else ERROR(CONFLICTING_QUORUM_START) 14 VALIDATE_DUMMY_IDENTIFIER(block) 15 VALIDATE_PRUNING_VOTE(block) Page 65 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba SET_PRUNING_STATE(block) 1 Dq ← RETRIEVE_DUMMY_BLOCK() 2 voters ← {} 3 if hk < (highest_start ← hs) 4 then return 5 if validated and 6 hk < GET_BLOCK_HEIGHT(READ_HIGHEST_VOTER(Dq)) 7 then return 8 h ← READ_BLOCK_HEIGHT(block) 9 descending ← {} 10 while hs 6 (h ← (h− 1)) 11 do Xs ← GET_PARENT_IDENTIFIER(block) 12 block ← RETRIEVE_BLOCK(Xs) 13 if h > hk 14 then ADD_TO_LIST(voters, Xs) 15 else if h = hk 16 then highest_start ← GET_START(block) 17 if highest_start = hs 18 then return 19 Xc ← Xs 20 else ADD_TO_LIST(descending, Xs) 21 routes ← GET_ROUTES(REVERT_LIST(descending)) 22 SET_HIGHEST_START(routes, descending) Page 66 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_START(block) 1 if hs > (height ← (READ_CHECKPOINT(block) + 1)) 2 then ERROR(CONFLICTING_CHECKPOINT_START) 3 if hs > (asks_low← READ_ASKS_LOW(block)) 4 then ERROR(CONFLICTING_ASKS-LOW_START) 5 if height = hs or asks_low = hs 6 then return hs 7 h ← READ_BLOCK_HEIGHT(block) 8 hm ← (asks_low− 1) 9 losses ← Lc ← 0 10 while h > asks_low or losses < Lc 11 do if h = hs 12 then ERROR(CONFLICTING_MONEY-TARGET_START) 13 block ← RETRIEVE_PARENT(block) 14 if asks_low > (h ← (h− 1)) 15 then if h = hm 16 then Lc ← READ_CURRENT_LOSS(block) 17 for each transaction in block 18 do zt ← GET_SIZE_IN_BYTES(transaction) 19 losses ← (losses + zt) 20 if h < height 21 then return h 22 return height SET_HIGHEST_START(routes, blocks) 1 h ← (hk − 1) 2 descending ← {} 3 ascending ← {} 4 child ← NIL 5 for each Xb in blocks 6 do block ← RETRIEVE_BLOCK(Xb) 7 ADD_TO_LIST(descending, Xb) 8 SET_START(routes, block, child, descending, ascending, h) 9 ascending ← REVERT_LIST(descending) 10 child ← block Page 67 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba SET_START(routes, block, child, descending, ascending, height) 1 if highest_start 6 (h ← READ_BLOCK_HEIGHT(block)) 2 then return 3 for each r in routes 4 do if ROUTE(r, block, child, descending, ascending, height) 5 then highest_start ← h 6 return ROUTE(r, block, child, descending, ascending, height) 1 if CHAINER_OR_LESSEE(r, block, ascending) 2 then h ← READ_BLOCK_HEIGHT(block) 3 lr ← GET_CHAINER_LOSS(block) 4 Wr ← GET_REWARD(block, child, lr, true) 5 return ((height − h)× lr) < Wr 6 if NIL = (output ← GET_OUTPUT(r, block)) 7 then return false 8 br ← GET_BALANCE(r, output, descending) 9 lr ← GET_ROUTE_LOSS(r, block) 10 return (br − GET_INACTIVITY_FEES(lr, ascending)) > 0 CHAINER_OR_LESSEE(rp, block, ascending) 1 r ← READ_CHAINER_IDENTIFIER(block) 2 if rp = GET_LAST_IDENTIFIER(r, block, ascending) 3 then return true 4 if NIL = (lease ← READ_LEASE(block)) 5 then return false 6 r ← READ_LESSEE_IDENTIFIER(lease) 7 return rp = GET_LAST_IDENTIFIER(r, block, ascending) GET_LAST_IDENTIFIER(r, block, ascending) 1 r ← GET_LAST_PROXY(r, block) 2 for each Xb in ascending 3 do block ← RETRIEVE_BLOCK(Xb) 4 r← GET_LAST_PROXY(r, block) 5 return r Page 68 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba GET_BALANCE(r, output, descending) 1 be ← READ_EXPLICIT_BALANCE(output) 2 for each Xb in descending 3 do block ← RETRIEVE_BLOCK(Xb) 4 earnings ← READ_EARNINGS_PER_OUTPUT(block) 5 for each output_quota in earnings 6 do if READ_EARNER_IDENTIFIER(output_quota) = r 7 then bi ← READ_OUTPUT_EARNINGS(output_quota) 8 return be + bi 9 return be ADD_PRUNING_VOTE(votes, block) 1 if NIL = (Xb ← READ_VOTED_START(block)) 2 then if NIL 6= (lease ← READ_LEASE(block)) 3 then Xb ← READ_PRE-VOTED_START(lease) 4 if Xb 6= NIL 5 then ADD_TO_LIST(votes, GET_BLOCK_HEIGHT(Xb)) GET_QUORUM() 1 return TRIM_TO_INTEGER(GET_LENGTH(voters) ÷ 2) + 1 GET_QUORUM_START(votes) 1 return TRIM_TO_INTEGER(GET_MEDIAN(votes)) Page 69 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba CREATE_DUMMY_BLOCK(block) 1 D ← CREATE_EMPTY_DUMMY_BLOCK() 2 b← RETRIEVE_BLOCK(Xc) 3 while quorum_start 6 (h ← READ_BLOCK_HEIGHT(b)) 4 do D ← SET_IMMUTABLE_BLOCK(D, b) 5 b← RETRIEVE_PARENT(b) 6 if h = quorum_start 7 then SET_DUMMY_DATA(D, b, block) 8 N ← GET_HASH(GET_DUMMY_DATA(D)) 9 WRITE_DUMMY_HASH(D, N) 10 return D SET_IMMUTABLE_BLOCK(D, block) 1 Xb ← GET_BLOCK_IDENTIFIER(block) 2 lr ← GET_CHAINER_LOSS(block) 3 if loss_height < quorum_start 4 then WRITE_CHAINER_LOSS(D, Xb, lr) 5 WRITE_LOSS_HEIGHT(D, Xb, loss_height) 6 WRITE_IMMUTABLE_BLOCK(D, Xb) SET_DUMMY_DATA(D, b, block) 1 lr ← GET_CHAINER_LOSS(b) 2 WRITE_DUMMY_REWARD(D, GET_REWARD(b, NIL, lr, true)) 3 WRITE_DUMMY_GIFT_RIGHTS(D, READ_GIFT_RIGHTS(b)) 4 WRITE_DUMMY_CURRENT_LOSS(D, READ_CURRENT_LOSS(b)) 5 WRITE_DUMMY_TIME(D, READ_BLOCK_TIME(b)) 6 WRITE_HIGHEST_VOTER(D, GET_PARENT_IDENTIFIER(block)) VALIDATE_HIGHEST_VOTER(block) 1 Xb ← GET_PARENT_IDENTIFIER(block) 2 if READ_HIGHEST_VOTER(Dq) 6= Xb 3 then ERROR(CONFLICTING_HIGHEST_VOTER) Page 70 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_DUMMY_IDENTIFIER(block) 1 Xb ← READ_DUMMY_IDENTIFIER(block) 2 if not quorum 3 then if Xb 6= NIL 4 then ERROR(ILLEGAL_DUMMY_REFERENCE) 5 return 6 N ← READ_DUMMY_HASH(Dq) 7 if Xb 6= CREATE_BLOCK_IDENTIFIER(N, quorum_start − 1) 8 then ERROR(INVALID_DUMMY_IDENTIFIER) VALIDATE_PRUNING_VOTE(block) 1 if NIL = (Xb ← GET_PRUNING_VOTE(block)) or 2 (not validated and not quorum) 3 then return 4 if NIL = (b← RETRIEVE_BLOCK(Xb)) 5 then ERROR(NONEXISTENT_VOTED_START) 6 h← READ_BLOCK_HEIGHT(b) 7 if h 6 quorum_start or h > highest_start 8 then ERROR(ILLEGAL_VOTED_START) GET_PRUNING_VOTE(block) 1 Xb ← READ_VOTED_START(block) 2 if NIL 6= (lease ← READ_LEASE(block)) 3 then chainer_vote ← (Xb 6= NIL) 4 Xb ← READ_PRE-VOTED_START(lease) 5 if Xb 6= NIL and chainer_vote 6 then ERROR(DOUBLE_PRUNING_VOTE) 7 return Xb VALIDATE_CHAIN_STATE(block) 1 VALIDATE_GENERAL_MAX_HASH(block) 2 VALIDATE_ASKS_LOW(block) 3 VALIDATE_CHECKPOINT(block) 4 VALIDATE_MONEY_SUPPLY(block) Page 71 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_GENERAL_MAX_HASH(block) 1 tb ← READ_BLOCK_TIME(block) 2 tp ← READ_BLOCK_TIME(RETRIEVE_PARENT(block)) 3 ib ← (tb − tp) 4 dw ← (READ_BLOCK_HEIGHT(block) − hw) 5 N ← (xg × (1 + (((ib ÷ Ib)− 1)÷ dw))) 6 if READ_GENERAL_MAX_HASH(block) 6= N 7 then ERROR(INVALID_GENERAL_DIFFICULTY_TARGET) VALIDATE_ASKS_LOW(block) 1 h← low← height ← READ_BLOCK_HEIGHT(b← block) 2 while hw 6 (h ← (h− 1)) 3 do b← RETRIEVE_PARENT(child ← b) 4 lr ← GET_CHAINER_LOSS(b) 5 Wr ← GET_REWARD(b, child, lr, true) 6 if ((height − h)× lr) < Wr 7 then low← h 8 if READ_ASKS_LOW(block) 6= low 9 then ERROR(INVALID_ASKS_LOW) VALIDATE_CHECKPOINT(block) 1 h ← READ_BLOCK_HEIGHT(b← block) 2 Lw ← 0 3 while h > hs 4 do Lw ← (Lw + GET_CHAINER_LOSS(b)) 5 if hs 6 (h ← (h− 1)) 6 then b← RETRIEVE_PARENT(b) 7 Lc ← READ_CURRENT_LOSS(b) 8 else D ← RETRIEVE_DUMMY_BLOCK() 9 Lc ← READ_DUMMY_CURRENT_LOSS(D) 10 if Lw > Lc 11 then if READ_CHECKPOINT(block) 6= h 12 then ERROR(INVALID_CHECKPOINT) 13 return 14 ERROR(MISSING_CHECKPOINT) Page 72 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba VALIDATE_MONEY_SUPPLY(block) 1 if READ_MONEY_SUPPLY(block) 6= (Mc + Mv) 2 then ERROR(INVALID_MONEY_SUPPLY) UPDATE_CHAIN(block) 1 UPDATE_GLOBAL_STATE(block) 2 PERSIST_BLOCK(GET_BLOCK_IDENTIFIER(block), block) 3 if READ_BLOCK_HEIGHT(block) > hc 4 then COMMIT() UPDATE_GLOBAL_STATE(block) 1 UPDATE_CURRENT_HEIGHT(block) 2 UPDATE_STARTING_HEIGHT() UPDATE_CURRENT_HEIGHT(block) 1 if READ_BLOCK_HEIGHT(block) > hc 2 then PERSIST_CURRENT_HEIGHT(hc + 1) UPDATE_STARTING_HEIGHT() 1 if not quorum 2 then return 3 if quorum_start > hs 4 then if RETRIEVE_PRECEDING_START() 6= hs 5 then UPDATE_PRECEDING_START() 6 PERSIST_DUMMY_BLOCK(Dq) 7 PERSIST_STARTING_HEIGHT(quorum_start) 8 return 9 PERSIST_VALIDATED(true) Page 73 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba UPDATE_PRECEDING_START() 1 if NIL 6= (D ← RETRIEVE_PREVIOUS_DUMMY()) 2 then ARCHIVE_PREVIOUS_DUMMY(hs, D) 3 b← RETRIEVE_BLOCK(Xs) 4 while NIL 6= (b← RETRIEVE_PARENT(b)) 5 do PRUNE_BLOCK(b, true) 6 D← RETRIEVE_DUMMY_BLOCK() 7 PERSIST_PREVIOUS_DUMMY(D) 8 PERSIST_PRECEDING_START(hs) PRUNE_BLOCK(block, archive) 1 if archive 2 then ARCHIVE_BLOCK(hs, block) 3 Xb ← GET_BLOCK_IDENTIFIER(block) 4 REMOVE_BLOCK(Xb) 5 REMOVE_PROXY_MAP(Xb) VALIDATE_BRANCH(block) 1 if validated 2 then VALIDATE_BRANCH_START(block) 3 VALIDATE_BLOCK(block) 4 while READ_BLOCK_HEIGHT(block) < hc 5 do if NIL = (block ← GET_CANDIDATE_CHILD(block)) 6 then ERROR(INCOMPLETE_BRANCH) 7 VALIDATE_BLOCK(block) VALIDATE_BRANCH_START(block) 1 D← RETRIEVE_DUMMY_BLOCK() 2 h← GET_BLOCK_HEIGHT(READ_HIGHEST_VOTER(D)) 3 if READ_BLOCK_HEIGHT(block) > h 4 then return 5 if not RESTORE_PREVIOUS_DUMMY() 6 then ERROR(BRANCH_NOT_ABOVE_PRUNING_VOTERS) 7 VALIDATE_BRANCH_START(block) Page 74 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba RESTORE_PREVIOUS_DUMMY() 1 if NIL = (start ← RETRIEVE_PRECEDING_START()) 2 then return false 3 if start < hs 4 then D← RETRIEVE_PREVIOUS_DUMMY() 5 PERSIST_DUMMY_BLOCK(D) 6 PERSIST_STARTING_HEIGHT(hs ← start) 7 return true 8 return false GET_CANDIDATE_CHILD(block) 1 Xb ← GET_BLOCK_IDENTIFIER(block) 2 if NIL = (child ← GET_CHILD_FROM_PEERS(Xb)) 3 then return NIL 4 if not VALID_BLOCK_FORMAT(child) or 5 not VALID_BLOCK_HASH(child) or 6 current_time < (time ← READ_BLOCK_TIME(child)) 7 then return NIL 8 h ← READ_BLOCK_HEIGHT(child) 9 N ← READ_PARENT_HASH(child) 10 if READ_BLOCK_HEIGHT(block) = (h− 1) and 11 READ_BLOCK_HASH(block) = N and 12 READ_BLOCK_TIME(block) < time 13 then return child 14 return NIL Page 75 of 76 Block Validation Pseudocode Proof-of-Loss Mirelo Deugh Ausgam Valis cba PRUNE_ORPHANED_BRANCHES() 1 h ← hc 2 pruned ← false 3 pruning ← true 4 while pruning 5 do siblings ← RETRIEVE_BLOCKS_AT_HEIGHT(h ← (h− 1)) 6 pruning ← false 7 for each sibling in siblings 8 do if not HAS_CHILDREN(sibling) 9 then PRUNE_BLOCK(sibling, false) 10 pruned ← pruning ← true 11 if pruned 12 then COMMIT() HAS_CHILDREN(block) 1 h← READ_BLOCK_HEIGHT(block) 2 siblings ← RETRIEVE_BLOCKS_AT_HEIGHT(h + 1) 3 N ← READ_BLOCK_HASH(block) 4 for each sibling in siblings 5 do if READ_PARENT_HASH(sibling) = N 6 then return true 7 return false Page 76 of 76 Block Validation Pseudocode