zawy12 / difficulty-algorithms

See the Issues for difficulty algorithms
MIT License
107 stars 25 forks source link

DGW (Dark Gravity Wave) Problems #31

Open zawy12 opened 5 years ago

zawy12 commented 5 years ago

Dark Gravity Wave problems:

  1. The complicated core [code] in the main loop is just a simple moving average of the targets that gives double weight to the most recent target, which is just an error (see math below).
  2. It uses 1 less timestamp than it is supposed to [code] causing it to have a 1/24 longer avg solvetime than expected when N=24.
  3. By having a small N=24, there is an additional 1.5% avg longer solvetime error. This is from solvetimes being more likely to be fast than slow (the Erlang or gamma is the distribution for N samples of the exponential distribution).
  4. Timespan limits 3x and 1/3 [code] allow a selfish mine to get unlimited blocks in 2x the averaging window if the timestamps are not sequential or otherwise limited in the allowed range. See timestamp attacks article.

2 and 3 cause solvetime to be 6% longer than expected.

# Simple moving average difficulty algorithm
# timestamps[1] = most recent timestamp
next_target = average(N past targets) * (timestamps[1]-timestamps[N+1])/(N*desired_block_time)

Instead of average of past N targets, DGW does this:

# Dark gravity wave "average" Targets
avgTarget = Target[1];
for ( i=2 ; i <= N; i++) {
  avgTarget =  ( avgTarget*i +Target[i] ) / (i+1) ;   
}

There's nothing in the comments to explain what this is supposed to be doing, so I expanded it out for i =2 to 4 (N=4). Target[1] = T1 = previous block's target and T4 is 4th block in the past. image which simplifies to: image This is just an average that gives double weight to the most recent target.

Here's the actual code.

for (unsigned int nCountBlocks = 1; nCountBlocks <= nPastBlocks; nCountBlocks++) {
        arith_uint256 bnTarget = arith_uint256().SetCompact(pindex->nBits);
        if (nCountBlocks == 1) {
            bnPastTargetAvg = bnTarget;
        } else {
            // NOTE: that's not an average really...
            bnPastTargetAvg = (bnPastTargetAvg * nCountBlocks + bnTarget) / (nCountBlocks + 1);
        }
        if(nCountBlocks != nPastBlocks) {
            assert(pindex->pprev); // should never fail
            pindex = pindex->pprev;
        }
    }

And what it should be:

for (unsigned int i = 1; i <= nPastBlocks; i++) {
        arith_uint256 bnTarget = arith_uint256().SetCompact(pindex->nBits);
        bnPastTargetAvg += bnTarget/nPastBlocks; // simple average
        pindex = pindex->pprev;   
}