qwertycoin-org / qwertycoin

Qwertycoin is a decentralized peer-to-peer protocol for safe payments worldwide.
https://qwertycoin.org
MIT License
33 stars 29 forks source link

EPoSe Movement #1 - EPoW : Proposing Changes in Block Reward Algorithm #78

Closed Xecute0101 closed 4 years ago

Xecute0101 commented 5 years ago

This is the first proposal to change our reward system for Qwertycoin Network.

Purpose: Qwertycoin aims to incentivize/disincentivize the network through a systematic approach by adjusting equations related to block reward calculations.

Current Status:

  1. Basic Block Reward Emission Equation (Abbreviated): ((Total Supply - Previously Emitted Supply) / Emission Factor) - Penalty for Block Size

Proposed Change:

  1. Basic Block Reward Emission Equation (Abbreviated): (((Total Supply - Previously Emitted Supply) / Emission Factor) - Penalty for Block Size) * ((Timestamp of New Block - Timestamp of Previous Block) / Block Target)

Premises:

  1. The block reward is designed to decrease per each block based on the previous emission.
  2. Since Basic Block Reward Emission is controlled and reduced to a fraction of normal block reward from such attacks, the overall block reward reduction will not be affected by this change.

Implications of This Change:

  1. By reinforcing the consistency of block reward emission over time (instead of block rewards by its sequence in the blockchain), the network hash rate is expected to be supported continuously and find an equilibrium automatically.
  2. Even if an attacker creates a high difficulty for the next block and exit, existing miners are incentivized to receive a huge block reward when they find the next block.
  3. Prevention of attacks with intermittent hash rate jumps must be dealt in a separate issue.
Xecute0101 commented 5 years ago

There shall be lower and upper bounds for this timestamp factor. Considering 2 for upper bound and 0 for lower bound.

Xecute0101 commented 5 years ago

The difficulty algorithm will be modified that it will help to keep the block time as consistent as possible.

aivve commented 5 years ago

You may find something usable here concerning punishing miners for deviation from T https://github.com/seredat/karbowanec/commit/231db5270acb2e673a641a1800be910ce345668a#commitcomment-23983192

Xecute0101 commented 5 years ago

I am leaning towards fixing difficulty algo for the efficiency of blockchain instead of controlling the number of blocks:

  1. If there are many pending txs, for an example, under a massive tx attack or we have txs from a lot of users that goes over the maximum size of a transaction set in the config file, diff algo should lowered difficulty to allow those to be included in the blocks and help reduce network backlog and mempool loads. Together with the reward penalty, malicious miners will be penalized for the size of the block, short duration of timestamp between blocks for zero gain.
aivve commented 5 years ago

Joining and synchronizing node can't verify that at the time some block was mined mempool was overcrowded. You can only estimate by taking into account transactions from previous blocks. If transaction count is above median you can lower the difficulty, this way it will be verifiable by the algorithm.

Xecute0101 commented 5 years ago

Joining and synchronizing node can't verify that at the time some block was mined mempool was overcrowded. You can only estimate by taking into account transactions from previous blocks. If transaction count is above median you can lower the difficulty, this way it will be verifiable by the algorithm.

So for this, an index of mempool status at the time of the most recent block can be included in the block header blob like an index figure. Is this what you mean?

aivve commented 5 years ago

Yes, but this is an overhead impossible to agree upon and verify, whereas you already have required data that allow you to estimate the demand for transactions - their count in previous blocks. Create a median of transaction count and use it to adjust difficulty to speedup blocks so you can include transactions faster. But this duplicates the job done by variable block size - if there is demand for transactions block size will grow to accommodate. There's no need to speed up block time.

From the other hand thus you will have two mechanisms to cope with the upsurge of transactions in the case of rapid adoption or usage.

Xecute0101 commented 5 years ago

This difficulty adjustment will only activate when total pending tx size is beyond max block size. Now when it is below 1mb(current limit).

Even in case when attackers are posting tons of txs to fill up the mempool and lower difficulty for more block rewards, the proposed changes in block rewards will prevent them from any gains. They will just spend a lot of coins.

Yet this difficulty adjustment will improve the efficiency in handling large amount of traffic. To protect nodes from excessive memory use, using database structure such as RocksDB or LMDB for txs in mempool will definitely improve the overall stability.

nnian commented 5 years ago

Is Basic Block Reward Emission Equation (Abbreviated): (((Total Supply - Previously Emitted Supply) / Emission Factor) - Penalty for Block Size) * ((Timestamp of New Block - Timestamp of Previous Block) / Block Target) the desired new formula for calculating the next block reward? Or do we have to change the formula again?

Xecute0101 commented 5 years ago

we just need to multiply ((timestamp of new block - timestamp of previous block)/blocktarget) to existing reward formula.

Xecute0101 commented 5 years ago

I propose to create a variable, called Consistency = ((Timestamp of New Block - Timestamp of Previous Block) / Block Target) and set the minimum and maximum range for this variable between 0 and 2.

Xecute0101 commented 5 years ago

@speqtr will work on the implementation.

speqtr commented 5 years ago

Updated block reward formula implementation is available in PR #99.

speqtr commented 5 years ago

In addition to lower and upper limits (0 and 2) for consistency, I propose to add "smart rounding" for all the values within 0..2 range. This way it will make consistency value more predictable. For example:

Xecute0101 commented 5 years ago

Thanks for the input @speqtr.

I think that consistency is just a reflection of what the entire network is doing, So rounding may not be necessary at this point so let's keep that point open to see how network behaves and decide whether to implement rounding or not.

But the number of decimals for consistency may need to be declared. 3 decimal points would be appropriate at the moment, meaning 4th decimal will be rounded to 3rd decimal points.

We need to see how this equation behaves to our current hashrate pattern before deploying. Can you please check? @nnian

speqtr commented 5 years ago

@nnian I can see that PR #99 was merged first and then instantly reverted (removed from master branch). Do I need to reopen it? Or will you manually re-add changes from PR #99 later?

nnian commented 5 years ago

Maybe you'll open it again and we'll discuss my concerns? With an implementation of a update height. What do you think about? I had commented on your PR a few times just a few minutes ago.

speqtr commented 5 years ago

@nnian later today I'll open new PR with the same changes as in PR #99 so we could add "activation height" for new block reward formula and then merge it once again.

nnian commented 5 years ago

Okay, sounds great. I think we shouldn't change the block reward without the assurance that every user has the same daemon version.

speqtr commented 5 years ago

New PR containing updated block reward formula is available. PR #101.

Xecute0101 commented 5 years ago

What this change entails...

General PoW Condition

The above graph shows how block reward emission has been for all PoWs. Heavily relying on difficulty to get to that green zone to make the block time and thereby block reward consistent. Miners were able to attack block rewards by utilizing massive hashrate to find blocks in a short period of time.

EPoSe PoW Condition

EPoSe PoW will change this like above graph with a maximum cap up to 2X normal rewards for longer solve time but almost zero block rewards in case of any timestamp attacks by posting many blocks in a short period of time.

With this solution in place, miners will be awarded proportionally to their contribution time to QWC blockchain. Also the block reward emission will be consistent over time. This "time" is the essence of all EPoSe implementations.

Difficulty algo is based on expectation based on previous history whereas this block reward algo is based on the results miners post. Diff algo is very difficult or almost impossible to be precise when there are sudden changes in miners' behavior. EPoSe block reward algo will tame this side effect in short.

This is a major step in order to make PoW part of EPoSe(Egalitarian Proof of Service) fair for all miners and users.

Thanks for the work and I think we can close this issue until we have a problem from this change.

speqtr commented 5 years ago

Thanks for the work and I think we can close this issue until we have a problem from this change.

@Xecute0101 Hopefully, we won't have any problems with this change 🙂

aivve commented 5 years ago

This makes miners to forge timestamps as close to target as possible to get max reward. The way CN pools operate they are not ready for this, they normally prepare block template with timestamp once and issue it to miners to hash, therefore every CN block has timestamp like one block behind (close to prev. block real solution time). This is easy to fix in pool software (in modified daemon in fact), instead of current timestamp during block template creation they will use prev block timestamp + target. Some fast and large miner may forge timestamps ahead from real time hitting FTL limit, I don't know what happens then and for other honest miners. What happens if miner forges timestamp ahead and then honest miner's solve time becomes negative?

Xecute0101 commented 5 years ago

Let's try to make a small change to the equation.

Previously, the most recent change in the formula is like this.


If consistency is less than equal to 1,

New Basic Block Reward Emission Equation (Abbreviated) = Basic Block Reward Emission Equation (previous equation) x consistency

=> If they solve the block too quickly with less effort, miners will get proportionally less rewards. So no more luck factor. If miners try to forge timestamps of blocks close to each other to get more block rewards, they will be penalized.


Else if consistency is greater than 1 and if ((difficulty windows * difficulty target) - (total timestamp difference for the duration of difficulty windows)) is greater than or equal to 0,

Basic Block Reward Emission Equation (previous equation) x maximum between consistency or (1+ min.((((difficulty windows * difficulty target) - (total timestamp difference for the duration of difficulty windows))/(difficulty target))), 1)

=> If miners or pools try to forge the timestamp before pool operates, it will be compared to range of block's timestamp to see how much time gap there is to allow additional reward. This is the solution for honest miners.


Else if consistency is greater than 1 and if ((difficulty windows * difficulty target) - (total timestamp difference for the duration of difficulty windows)) is less than 0,

Basic Block Reward Emission Equation (previous equation) x 0.0001

=> If miners or pools try to forge the timestamp before pool operates, it will be compared to range of block's timestamp to see how much time gap there is to allow additional reward. If they go over the range of time then all miners will be penalized. So this will force miners and pools to correct the timestamp forging.

In case the hashrate is actually dropping due to less popularity or network issue, this part of equation will not cause any harmful effect as the issuance of new coins will be restricted until the network finds the new equilibrium.


If some miner forge timestamp and the honest miner finds it faster, then miner who forge timestamp will get orphan block. There is really no incentive for them to try to do this. They may succeed in a block or two but will fail shortly after. This may not be the perfect fix but it will serve its purpose for the time being.

Xecute0101 commented 5 years ago

Zawy, the difficulty algo dev mentioned this in PR #99 that

But make sure out of sequence timestamps are handled correctly:


// Safely handle out-of-sequence timestamps
if ( timestamps[i]  >= previous_timestamp ) {   this_timestamp = timestamps[i];  } 
else {  this_timestamp = previous_timestamp+1;   }

Do we have this measure in place? If not, we should. @speqtr @Sw0rdf1sh1 @nnian

nnian commented 5 years ago

@Xecute0101 I think we have no time to rewrite this PR again and again if we want to have the new reward formula up to network height 400,000. in 24k blocks would be the change. We still need time to create the new wallets, to distribute them and, if necessary, to provide assistance while every user update to this new wallet in time.

We should think about it.

nnian commented 5 years ago

Version 5.2.0 has been merged with the PR 101 in the master for 18 days now. The new Chinese Exchanges may already use this version. Also some pools have already changed. We have to decide soon. But we don't have time to change this formula just a few blocks before that soft fork date.

We should provide new wallets at least 14 days before we reach height 400k. I don't know what happens when there are different reward formulas available on the network and especially in various pools. We need a network with the same software version.

nnian commented 5 years ago

If there is a potential problem or even some minor changes with this merged calculation formula, I have to postpone and increase the Softfork height, revert version 5.2.0 and we need to provide 5.2.1 version and inform exchanges, pools, node owner and wallet user to update again to 5.2.1. After this we can talk about this formula and change whatever we want.

Xecute0101 commented 5 years ago

@nnian , Thanks for your opinion on this improvement.

I suggest we postpone this until block height 500,000. I hope that we can settle issues with all desktop and mobile wallets by block height 500,000 and move on with EPoSe implementation.

Xecute0101 commented 4 years ago

Had run a node that activated this at the height of 500,000 and this is the message we get.

2020-Feb-11 19:10:20.936531 INFO
[checkpoints] CHECKPOINT PASSED FOR HEIGHT 500000 432b0c6cfbb5967950efc2f2358f42dc85ddbc5dd270b619cd1f466970a41236

2020-Feb-11 19:10:20.936531 ERROR [Blockchain] Coinbase transaction spend too much money: 135845.06483386, block reward is 0.00000000

The gap between height 499,999 and 500,000 was about 67 seconds. So, if we did correct implementation of the formula, the block reward error should show that the reward should be

new reward = 135845.06483386 * consistency(which is 67sec/120sec) = 75,846.82786557

So it should be showing the following error message when correctly implemented,

2020-Feb-11 19:10:20.936531 ERROR [Blockchain] Coinbase transaction spend too much money: 135845.06483386, block reward is 75,846.82786557

bool Currency::getBlockReward(
    uint8_t blockMajorVersion,
    size_t medianSize,
    size_t currentBlockSize,
    uint64_t alreadyGeneratedCoins,
    uint64_t fee,
    uint64_t &reward,
    int64_t &emissionChange,
    uint32_t height,
    uint64_t blockTarget) const
{
    assert(alreadyGeneratedCoins <= m_moneySupply);
    assert(m_emissionSpeedFactor > 0 && m_emissionSpeedFactor <= 8 * sizeof(uint64_t));

    // Tail emission

    uint64_t baseReward = (m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor;
    if (alreadyGeneratedCoins + CryptoNote::parameters::TAIL_EMISSION_REWARD >= m_moneySupply
        || baseReward < CryptoNote::parameters::TAIL_EMISSION_REWARD) {
        baseReward = CryptoNote::parameters::TAIL_EMISSION_REWARD;
    }

    size_t blockGrantedFullRewardZone = blockGrantedFullRewardZoneByBlockVersion(blockMajorVersion);
    medianSize = std::max(medianSize, blockGrantedFullRewardZone);
    if (currentBlockSize > medianSize * UINT64_C(2)) {
        logger(TRACE)
            << "Block cumulative size is too big: "
            << currentBlockSize
            << ", expected less than "
            << medianSize * UINT64_C(2);
        return false;
    }

    uint64_t penalizedBaseReward = getPenalizedAmount(baseReward, medianSize, currentBlockSize);
    uint64_t penalizedFee = fee;
    if (blockMajorVersion >= BLOCK_MAJOR_VERSION_2 || cryptonoteCoinVersion() == 1) {
        penalizedFee = getPenalizedAmount(fee, medianSize, currentBlockSize);
    }

    double consistency = 1.0;
    if (height >= CryptoNote::parameters::UPGRADE_HEIGHT_REWARD_SCHEME && difficultyTarget() != 0) {
        // blockTarget is (Timestamp of New Block - Timestamp of Previous Block)
        consistency = blockTarget / difficultyTarget();

        // consistency range is 0..2
        consistency = std::max<double>(consistency, 0.0);
        consistency = std::min<double>(consistency, 2.0);
    }

    double penalizedReward = static_cast<double>(penalizedBaseReward + penalizedFee);

    emissionChange = penalizedBaseReward - (fee - penalizedFee);
    reward = static_cast<uint64_t>(penalizedReward * consistency);

    return true;
}
aivve commented 4 years ago

Besides this error you probably should fix emissionChange so it takes into account new penalizedReward not penalizedBaseReward.

aivve commented 4 years ago

Perhaps need to cast in consistency = blockTarget / difficultyTarget(); like consistency = (double)blockTarget / (double)difficultyTarget();

Xecute0101 commented 4 years ago

@speqtr @exploshot @nnian thought?

Xecute0101 commented 4 years ago

Also, the controversial part is going to interfere when we get to tail emission. assert(alreadyGeneratedCoins <= m_moneySupply); @speqtr erased and it kinda raise a flag but it was a right decision.

aivve commented 4 years ago

Yes, it should be deleted

Xecute0101 commented 4 years ago

where are this penalizedReward and reward variables used? only declared here maybe?

aivve commented 4 years ago

penalizedReward is used in this function to calculate final reward, but emissionChange is also used elsewhere so it is important that it returns the correct value.

speqtr commented 4 years ago

@Xecute0101 @aivve I can implement discussed changes and open a new PR, so we could continue dicussing and testing proposed changes at the same time 🙂

aivve commented 4 years ago

I just realized that you probably are making totally different scheme than I thought, the one that was also suggested by @zawy12 - if the solve time is smaller than target time then reward is proportionally smaller AND vice versa - if solve time is longer, then reward is bigger. @Xecute0101 this then changes the incentives for miners to artificially create as long gaps as they could to maximize the reward. Of course, they will hit the FTL limit how far timestamp can go into the future and you then should really tighten that limit. (I thought that you are going to punish miners for deviation from target solve time in ANY direction so they will be incetivized to keep solve times as close to target as possible).

Xecute0101 commented 4 years ago

Yes, we talked about this. The reward gets bigger if they miners mine longer in a sense. Have to look into FTL and Difficulty Algo now. Unless I find a way to distribute hashpower equally among parties.

aivve commented 4 years ago

Just tighten the limits so they can't fake timestamps.

Xecute0101 commented 4 years ago

We need to get this calculation right, first I think. If anyone of you is going to test this, change the height in the config and try to see the error msg displays correct rewards.

aivve commented 4 years ago

Also originally doubles were avoided in CryptoNote... Perhaps find a way to calculate this using integer math.

zawy12 commented 4 years ago

Increasing reward based on solvetime has the same effect as decreasing difficulty. Miners are only interested in getting the highest reward/difficulty ratio.

Another option I've been thinking about (that deadalnix and jl777 like) actually depends on on-off mining. The idea is that your dedicated miners can be identified as being the ones who are still mining when the difficulty is high. Ostensibly, they will not attack the coin and you want them to win in a chain race. They paid a higher price in the past so give them something extra to win chain races. As an example plan, if difficulty is more than 1 std deviation (16% of blocks) above the 1 day average, then credit the miner's output address with 1/2 of the above-average difficulty. This can be thought of as a 2nd coin that can't be sent to any other address. If there is a battle between 2 or more tips with at least 3 blocks each, then miners with this coin saved up from the past can use it to artificially lower difficulty to win a chain race. They can "get back" 1/2 of the excess difficulty they spent on blocks that were in the the upper 16% of difficulties. It specifically is NOT meant to motivate the mining of higher difficulty. We want higher difficulty so that we can identify our friends, and let them win chain races. A limit might be placed on the amount of "credit" they can get, and the credit might deteriorate over a year. So if you have 10% of your miners really dedicated, and 16% of your difficulty swings everyday are 20% too high, then that 10% of miners are getting 16% / 2 * 20% = 1.6% of the day's chain work back as credit. In 100 days they will 160% of a days chain work to fight attacks.

I need to do an issue-article on this.

Xecute0101 commented 4 years ago

Yes, it should be deleted

Or just in case, commenting out the line would be okay too.

Xecute0101 commented 4 years ago

2020-Feb-12 15:50:23.631190 ERROR Coinbase transaction spend too much money: 135845.06483386, block reward is 67922.53241693

This happened when consistency was not used and I wanted to test out where to put this consistency variable. It just happens to be right after baseReward. I just did "*0.5" like this.

uint64_t baseReward = ((m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor) * 0.5;

bool Currency::getBlockReward(
    uint8_t blockMajorVersion,
    size_t medianSize,
    size_t currentBlockSize,
    uint64_t alreadyGeneratedCoins,
    uint64_t fee,
    uint64_t &reward,
    int64_t &emissionChange,
    uint32_t height,
    uint64_t blockTarget) const
{
    assert(alreadyGeneratedCoins <= m_moneySupply);
    assert(m_emissionSpeedFactor > 0 && m_emissionSpeedFactor <= 8 * sizeof(uint64_t));

    // Consistency

    double consistency = 1.0;
    if (height >= CryptoNote::parameters::UPGRADE_HEIGHT_REWARD_SCHEME && difficultyTarget() != 0) {
        // blockTarget is (Timestamp of New Block - Timestamp of Previous Block)
        consistency = blockTarget / difficultyTarget();

        // consistency range is 0..2
        consistency = std::max<double>(consistency, 0.0);
        consistency = std::min<double>(consistency, 2.0);
    }

    // Tail emission

    uint64_t baseReward = ((m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor) * 0.5;
    if (alreadyGeneratedCoins + CryptoNote::parameters::TAIL_EMISSION_REWARD >= m_moneySupply
        || baseReward < CryptoNote::parameters::TAIL_EMISSION_REWARD) {
        baseReward = CryptoNote::parameters::TAIL_EMISSION_REWARD;
    }

    size_t blockGrantedFullRewardZone = blockGrantedFullRewardZoneByBlockVersion(blockMajorVersion);
    medianSize = std::max(medianSize, blockGrantedFullRewardZone);
    if (currentBlockSize > medianSize * UINT64_C(2)) {
        logger(TRACE)
            << "Block cumulative size is too big: "
            << currentBlockSize
            << ", expected less than "
            << medianSize * UINT64_C(2);
        return false;
    }

    uint64_t penalizedBaseReward = getPenalizedAmount(baseReward, medianSize, currentBlockSize);
    uint64_t penalizedFee = fee;
    if (blockMajorVersion >= BLOCK_MAJOR_VERSION_2 || cryptonoteCoinVersion() == 1) {
        penalizedFee = getPenalizedAmount(fee, medianSize, currentBlockSize);
    }

    uint64_t penalizedReward = static_cast<uint64_t>(penalizedBaseReward + penalizedFee);

    emissionChange = penalizedReward - fee;
    reward = static_cast<uint64_t>(penalizedReward);

    return true;

I also tried to multiply consistency in place of 0.5, then the block reward was 0.0000000000. This means that the consistency is returning 0.

2020-Feb-13 07:01:37.370251 ERROR Coinbase transaction doesn't use full amount of block reward: spent 135845.06483386, block reward is 16301407.78006320

Above happens when difficultyTarget() is multiplied in place of 0.5 meaning the value of difficultyTarget() is 120 and it is returning value properly.

2020-Feb-13 07:05:48.258607 ERROR Coinbase transaction doesn't use a full amount of block reward: spent 135845.06483386, block reward is 9101619.34386862

Above happens when blockTarget() is multiplied in place of 0.5 meaning the value of blockTarget() is 67 and it is returning value properly.

Xecute0101 commented 4 years ago

When Coinbase transaction is less than reward (minerReward<reward), following msg should be displayed per Blockchain::validate_miner_transaction() in Blockchain.cpp file.

if (minerReward > reward) {
        logger(ERROR, BRIGHT_RED)
            << "Coinbase transaction spend too much money: " << m_currency.formatAmount(minerReward)
            << ", block reward is " << m_currency.formatAmount(reward);

2020-Feb-13 07:15:41.977399 ERROR Coinbase transaction spend too much money: 135845.06483386, block reward is 75846.82786557

The above happens when the following code changed.

consistency = (double)blockTarget / (double)difficultyTarget();

uint64_t baseReward = ((m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor) * consistency;

When Coinbase transaction is greater than reward (minerReward>reward), the following msg should be displayed per Blockchain::validate_miner_transaction() in Blockchain.cpp file.

else if (minerReward < reward) {
        logger(ERROR, BRIGHT_RED)
            << "Coinbase transaction doesn't use full amount of block reward: spent "
            << m_currency.formatAmount(minerReward)
            << ", block reward is "
            << m_currency.formatAmount(reward);

Correct messages were displayed from the examples of previous comment.

Xecute0101 commented 4 years ago

The working code looks like

bool Currency::getBlockReward(
    uint8_t blockMajorVersion,
    size_t medianSize,
    size_t currentBlockSize,
    uint64_t alreadyGeneratedCoins,
    uint64_t fee,
    uint64_t &reward,
    int64_t &emissionChange,
    uint32_t height,
    uint64_t blockTarget) const
{
    // assert(alreadyGeneratedCoins <= m_moneySupply);
    assert(m_emissionSpeedFactor > 0 && m_emissionSpeedFactor <= 8 * sizeof(uint64_t));

    // Consistency

    double consistency = 1.0;
    if (height >= CryptoNote::parameters::UPGRADE_HEIGHT_REWARD_SCHEME && difficultyTarget() != 0) {
        // blockTarget is (Timestamp of New Block - Timestamp of Previous Block)
        consistency = (double) blockTarget / (double) difficultyTarget();

        // consistency range is 0..2
        consistency = std::max<double>(consistency, 0.0);
        consistency = std::min<double>(consistency, 2.0);
    }

    // Tail emission

    uint64_t baseReward = ((m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor) * consistency;
    if (alreadyGeneratedCoins + CryptoNote::parameters::TAIL_EMISSION_REWARD >= m_moneySupply
        || baseReward < CryptoNote::parameters::TAIL_EMISSION_REWARD) {
        baseReward = CryptoNote::parameters::TAIL_EMISSION_REWARD;
    }

    size_t blockGrantedFullRewardZone = blockGrantedFullRewardZoneByBlockVersion(blockMajorVersion);
    medianSize = std::max(medianSize, blockGrantedFullRewardZone);
    if (currentBlockSize > medianSize * UINT64_C(2)) {
        logger(TRACE)
            << "Block cumulative size is too big: "
            << currentBlockSize
            << ", expected less than "
            << medianSize * UINT64_C(2);
        return false;
    }

    uint64_t penalizedBaseReward = getPenalizedAmount(baseReward, medianSize, currentBlockSize);
    uint64_t penalizedFee = fee;
    if (blockMajorVersion >= BLOCK_MAJOR_VERSION_2 || cryptonoteCoinVersion() == 1) {
        penalizedFee = getPenalizedAmount(fee, medianSize, currentBlockSize);
    }

    emissionChange = penalizedBaseReward - (fee - penalizedFee);
    reward = penalizedBaseReward + penalizedFee;

    return true;
}

@nnian @exploshot @speqtr @aivve

Xecute0101 commented 4 years ago

Added #include <cmath> and double exponent = 0.25; and changed consistency formula when it is above 1.

bool Currency::getBlockReward(
    uint8_t blockMajorVersion,
    size_t medianSize,
    size_t currentBlockSize,
    uint64_t alreadyGeneratedCoins,
    uint64_t fee,
    uint64_t &reward,
    int64_t &emissionChange,
    uint32_t height,
    uint64_t blockTarget) const
{
    // assert(alreadyGeneratedCoins <= m_moneySupply);
    assert(m_emissionSpeedFactor > 0 && m_emissionSpeedFactor <= 8 * sizeof(uint64_t));

    // Consistency
    double consistency = 1.0;
    double exponent = 0.25; 
    if (height >= CryptoNote::parameters::UPGRADE_HEIGHT_REWARD_SCHEME && difficultyTarget() != 0) {
        // blockTarget is (Timestamp of New Block - Timestamp of Previous Block)
        consistency = (double) blockTarget / (double) difficultyTarget();

        // consistency range is 0..2
        if (consistency < 1.0) {
            consistency = std::max<double>(consistency, 0.0);
        }
        else if (consistency > 1.0) {
            consistency = pow(consistency, exponent);
            consistency = std::min<double>(consistency, 2.0);
        }
        else {
            consistency = 1.0;
        }
    }  

    // Tail emission
    uint64_t baseReward = ((m_moneySupply - alreadyGeneratedCoins) >> m_emissionSpeedFactor) * consistency;
    if (alreadyGeneratedCoins + CryptoNote::parameters::TAIL_EMISSION_REWARD >= m_moneySupply
        || baseReward < CryptoNote::parameters::TAIL_EMISSION_REWARD) {
        baseReward = CryptoNote::parameters::TAIL_EMISSION_REWARD;
    }

    size_t blockGrantedFullRewardZone = blockGrantedFullRewardZoneByBlockVersion(blockMajorVersion);
    medianSize = std::max(medianSize, blockGrantedFullRewardZone);
    if (currentBlockSize > medianSize * UINT64_C(2)) {
        logger(TRACE)
            << "Block cumulative size is too big: "
            << currentBlockSize
            << ", expected less than "
            << medianSize * UINT64_C(2);
        return false;
    }

    uint64_t penalizedBaseReward = getPenalizedAmount(baseReward, medianSize, currentBlockSize);
    uint64_t penalizedFee = fee;
    if (blockMajorVersion >= BLOCK_MAJOR_VERSION_2 || cryptonoteCoinVersion() == 1) {
        penalizedFee = getPenalizedAmount(fee, medianSize, currentBlockSize);
    }

    emissionChange = penalizedBaseReward - (fee - penalizedFee);
    reward = penalizedBaseReward + penalizedFee;

    return true;
}
Xecute0101 commented 4 years ago

For mining rewards over 120 seconds, double exponent = 0.25; is used for raising consistency to a power of (1/4). The impact of this is shown in the attached excel file. EPoW.Worksheet R0.xlsx

Any extra time over difficulty target(120 sec) will get additional reward but does not provide enough incentives to forge a timestamp beyond the real time because it yields less profit than mining a next block less than 120 seconds.

image

The significance of QWC's EPoW is that it is a completely independent approach to influence miners' behaviors from difficulty adjustments and an egalitarian reward system to all mining environments.

I think we are ready to commit this to our main repo. Any devil's advocate? @nnian @speqtr @aivve @zawy12 @exploshot