Could you add MemoryCoin (MMC).
They used a bad parameter at the beginning which resulted in quite a big funny fluctuation, they then changed the algorithm, it's smooth now. Could be interesting.
Added MemoryCoin charts to the site:
http://cryptometer.org/memorycoin_96_hour_charts.htmlhttp://cryptometer.org/memorycoin_90_day_charts.htmlYes, that's interesting. You can see the fluctuation on the first chart. I guess they hard-forked at Hour 71? Sounds stressful! I'm going to read that giant thread and see what parameter they changed.
I found the fix in github. They replaced the entire algorithm. It's not a simple parameter change. I also compared the old algorithm in MemoryCoin with the current algorithms in Unobtanium and Zetacoin. They are the same. So that's the problem and how to fix it.
https://github.com/memorycoin/memorycoin/commit/05d26375cb17804bf2fa233678d0bb2dbea6321fThe old algorithm:
unsigned int static OldGetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock)
{
unsigned int nProofOfWorkLimit = bnProofOfWorkLimit.GetCompact();
// Genesis block
if (pindexLast == NULL)
return nProofOfWorkLimit;
//Change difficulty every block based on the previous nInterval blocks
//Timewarp bug fix
// Go back the full period unless it's the first retarget after genesis. Code courtesy of Art Forz
int nBlocksBack = nInterval-1;
if((pindexLast->nHeight+1) != nInterval)
nBlocksBack = nInterval;
const CBlockIndex* pindexFirst = pindexLast;
for (int i = 0; pindexFirst && i < nBlocksBack; i++)
pindexFirst = pindexFirst->pprev;
//assert(pindexFirst);
if(pindexFirst==NULL){
return nProofOfWorkLimit;
}
// Limit adjustment step
int64 nActualTimespan = pindexLast->GetBlockTime() - pindexFirst->GetBlockTime();
printf(" nActualTimespan = %"PRI64d" before bounds\n", nActualTimespan);
if (nActualTimespan < nTargetTimespan*0.8)
nActualTimespan = nTargetTimespan*0.8;
if (nActualTimespan > nTargetTimespan*1.2)
nActualTimespan = nTargetTimespan*1.2;
// Retarget
CBigNum bnNew;
bnNew.SetCompact(pindexLast->nBits);
bnNew *= nActualTimespan;
bnNew /= nTargetTimespan;
if (bnNew > bnProofOfWorkLimit)
bnNew = bnProofOfWorkLimit;
/// debug print
//printf("\n");
printf("nTargetTimespan = %"PRI64d" nActualTimespan = %"PRI64d"\n", nTargetTimespan, nActualTimespan);
printf("Before: %08x %s\n", pindexLast->nBits, CBigNum().SetCompact(pindexLast->nBits).getuint256().ToString().c_str());
printf("GetNextWorkRequired RETARGET After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str());
return bnNew.GetCompact();
}
The new algorithm:
unsigned int static KimotoGravityWell(const CBlockIndex* pindexLast, const CBlockHeader *pblock, uint64 TargetBlocksSpacingSeconds, uint64 PastBlocksMin, uint64 PastBlocksMax) {
/* current difficulty formula, Anoncoin - kimoto gravity well */
const CBlockIndex *BlockLastSolved = pindexLast;
const CBlockIndex *BlockReading = pindexLast;
const CBlockHeader *BlockCreating = pblock;
BlockCreating = BlockCreating;
uint64 PastBlocksMass = 0;
int64 PastRateActualSeconds = 0;
int64 PastRateTargetSeconds = 0;
double PastRateAdjustmentRatio = double(1);
CBigNum PastDifficultyAverage;
CBigNum PastDifficultyAveragePrev;
double EventHorizonDeviation;
double EventHorizonDeviationFast;
double EventHorizonDeviationSlow;
if (BlockLastSolved == NULL || BlockLastSolved->nHeight == 0 || (uint64)BlockLastSolved->nHeight < PastBlocksMin) { return bnProofOfWorkLimit.GetCompact(); }
for (unsigned int i = 1; BlockReading && BlockReading->nHeight > 0; i++) {
if (PastBlocksMax > 0 && i > PastBlocksMax) { break; }
PastBlocksMass++;
if (i == 1) { PastDifficultyAverage.SetCompact(BlockReading->nBits); }
else { PastDifficultyAverage = ((CBigNum().SetCompact(BlockReading->nBits) - PastDifficultyAveragePrev) / i) + PastDifficultyAveragePrev; }
PastDifficultyAveragePrev = PastDifficultyAverage;
PastRateActualSeconds = BlockLastSolved->GetBlockTime() - BlockReading->GetBlockTime();
PastRateTargetSeconds = TargetBlocksSpacingSeconds * PastBlocksMass;
PastRateAdjustmentRatio = double(1);
if (PastRateActualSeconds < 0) { PastRateActualSeconds = 0; }
if (PastRateActualSeconds != 0 && PastRateTargetSeconds != 0) {
PastRateAdjustmentRatio = double(PastRateTargetSeconds) / double(PastRateActualSeconds);
}
EventHorizonDeviation = 1 + (0.7084 * pow((double(PastBlocksMass)/double(144)), -1.228));
EventHorizonDeviationFast = EventHorizonDeviation;
EventHorizonDeviationSlow = 1 / EventHorizonDeviation;
if (PastBlocksMass >= PastBlocksMin) {
if ((PastRateAdjustmentRatio <= EventHorizonDeviationSlow) || (PastRateAdjustmentRatio >= EventHorizonDeviationFast)) { assert(BlockReading); break; }
}
if (BlockReading->pprev == NULL) { assert(BlockReading); break; }
BlockReading = BlockReading->pprev;
}
CBigNum bnNew(PastDifficultyAverage);
if (PastRateActualSeconds != 0 && PastRateTargetSeconds != 0) {
bnNew *= PastRateActualSeconds;
bnNew /= PastRateTargetSeconds;
}
if (bnNew > bnProofOfWorkLimit) { bnNew = bnProofOfWorkLimit; }
/// debug print
printf("Difficulty Retarget - Kimoto Gravity Well\n");
printf("PastRateAdjustmentRatio = %g\n", PastRateAdjustmentRatio);
printf("Before: %08x %s\n", BlockLastSolved->nBits, CBigNum().SetCompact(BlockLastSolved->nBits).getuint256().ToString().c_str());
printf("After: %08x %s\n", bnNew.GetCompact(), bnNew.getuint256().ToString().c_str());
return bnNew.GetCompact();
}
unsigned int static NeoGetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock)
{
static const int64 BlocksTargetSpacing = 6 * 60; // 6 minutes
unsigned int TimeDaySeconds = 60 * 60 * 24;
int64 PastSecondsMin = TimeDaySeconds*0.25;
int64 PastSecondsMax = TimeDaySeconds*1;
uint64 PastBlocksMin = PastSecondsMin / BlocksTargetSpacing;
uint64 PastBlocksMax = PastSecondsMax / BlocksTargetSpacing;
return KimotoGravityWell(pindexLast, pblock, BlocksTargetSpacing, PastBlocksMin, PastBlocksMax);
}